From 71cf1eb204eaef31dc11000b8babd7c6b009e867 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 13:51:55 -0800 Subject: [PATCH 001/121] Rejigger project names --- .../gen/LoggingGen.Parse.cs | 311 ++++++++++++++++++ .../gen/LoggingGen.cs | 281 ++++++++++++++++ ...osoft.Extensions.Logging.Generators.csproj | 17 + .../LoggingTests.cs | 258 +++++++++++++++ ...Extensions.Logging.Generators.Tests.csproj | 29 ++ .../MockLogger.cs | 67 ++++ 6 files changed, 963 insertions(+) create mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.Parse.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.Parse.cs new file mode 100644 index 0000000000000..16f1fc4d33f80 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.Parse.cs @@ -0,0 +1,311 @@ +// © Microsoft Corporation. All rights reserved. + +namespace LoggingGen +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + public partial class LoggingGenerator + { + private const string DiagnosticCategory = "R9LoggingGenerator"; + +#pragma warning disable RS2008 // Enable analyzer release tracking + + private static readonly DiagnosticDescriptor ErrorInvalidMethodName = new( + id: "LG0", + title: "Logging method names cannot start with __", + messageFormat: "Logging method names cannot start with __", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorInvalidMessage = new( + id: "LG1", + title: "Missing message for logging method", + messageFormat: "Missing message for logging method", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorInvalidParameterName = new( + id: "LG2", + title: "Logging method parameter names cannot start with __", + messageFormat: "Logging method parameter names cannot start with __", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorInvalidTypeName = new( + id: "LG3", + title: "Missing generated type name", + messageFormat: "Missing generated type name", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorNestedType = new( + id: "LG4", + title: "Logging interfaces cannot be in nested types", + messageFormat: "Logging interfaces cannot be in nested types", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorMissingAttributeType = new( + id: "LG5", + title: "Could not find a required attribute definition", + messageFormat: "Could not find definition for attribute {0}", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + /// + /// Gets the known set of annotated logger classes + /// + private static IEnumerable GetLogClasses(GeneratorExecutionContext context, Compilation compilation) + { + var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); + var allInterfaces = allNodes.Where(d => d.IsKind(SyntaxKind.InterfaceDeclaration)).OfType(); + + var logExtensionsAttribute = compilation.GetTypeByMetadataName("R9Logging.LoggerExtensionsAttribute"); + if (logExtensionsAttribute is null) + { + // emit a diagnostic about the attribute not being present but needing to be + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "R9Logging.LoggerExtensionsAttribute")); + yield break; + } + + var loggerMessageAttribute = compilation.GetTypeByMetadataName("R9Logging.LoggerMessageAttribute"); + if (loggerMessageAttribute is null) + { + // emit a diagnostic about the attribute not being present but needing to be + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "R9Logging.LoggerMessageAttribute")); + yield break; + } + + // Temp work around for https://github.com/dotnet/roslyn/pull/49330 + var semanticModelMap = new Dictionary(); + + foreach (var iface in allInterfaces) + { + foreach (var al in iface.AttributeLists) + { + foreach (var a in al.Attributes) + { + if (!semanticModelMap.TryGetValue(a.SyntaxTree, out var semanticModel)) + { + semanticModel = compilation.GetSemanticModel(a.SyntaxTree); + semanticModelMap[a.SyntaxTree] = semanticModel; + } + + // does this interface have the [LoggerExtensions] atribute? + var aSymbol = semanticModel.GetSymbolInfo(a, context.CancellationToken); + if (aSymbol.Symbol is IMethodSymbol methodSymbol && logExtensionsAttribute.Equals(methodSymbol.ContainingType, SymbolEqualityComparer.Default)) + { + NamespaceDeclarationSyntax? ns = null; + if (iface.Parent != null) + { + ns = iface.Parent as NamespaceDeclarationSyntax; + if (ns == null && iface.Parent is not CompilationUnitSyntax) + { + // since this generator doesn't know how to generate a nested type... + context.ReportDiagnostic(Diagnostic.Create(ErrorNestedType, iface.Identifier.GetLocation())); + } + } + + string? name = null; + if (a.ArgumentList?.Arguments.Count > 0) + { + var arg = a.ArgumentList!.Arguments[0]; + name = compilation.GetSemanticModel(a.SyntaxTree).GetConstantValue(arg.Expression).ToString(); + } + + if (name == null) + { + var ifaceName = iface.Identifier.ToString(); + if (ifaceName[0] == 'I' && ifaceName.Length > 1) + { + name = ifaceName.Substring(1); + } + else + { + name = ifaceName + "Extensions"; + } + } + + var lc = new LoggerClass + { + Namespace = ns?.Name.ToString(), + Name = name, + OriginalInterfaceName = iface.Identifier.ToString(), + }; + + if (string.IsNullOrWhiteSpace(lc.Name)) + { + context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidTypeName, a.GetLocation())); + } + + foreach (var method in iface.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) + { + foreach (var mal in method.AttributeLists) + { + foreach (var ma in mal.Attributes) + { + if (!semanticModelMap.TryGetValue(ma.SyntaxTree, out semanticModel)) + { + semanticModel = compilation.GetSemanticModel(ma.SyntaxTree); + semanticModelMap[ma.SyntaxTree] = semanticModel; + } + + var maSymbol = semanticModel.GetSymbolInfo(ma, context.CancellationToken); + if (maSymbol.Symbol is IMethodSymbol ms && loggerMessageAttribute.Equals(ms.ContainingType, SymbolEqualityComparer.Default)) + { + var arg = ma.ArgumentList!.Arguments[0]; + var eventId = compilation.GetSemanticModel(ma.SyntaxTree).GetConstantValue(arg.Expression).ToString(); + + arg = ma.ArgumentList!.Arguments[1]; + var level = compilation.GetSemanticModel(ma.SyntaxTree).GetConstantValue(arg.Expression).ToString(); + + arg = ma.ArgumentList!.Arguments[2]; + var message = compilation.GetSemanticModel(ma.SyntaxTree).GetConstantValue(arg.Expression).ToString(); + + var lm = new LoggerMethod + { + Name = method.Identifier.ToString(), + EventId = eventId, + Level = level, + Message = message, + MessageHasTemplates = HasTemplates(message), + }; + lc.Methods.Add(lm); + + if (lm.Name.StartsWith("__", StringComparison.Ordinal)) + { + // can't have logging method names that start with __ since that can lead to conflicting symbol names + // because the generated symbols start with __ + context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodName, method.Identifier.GetLocation())); + } + + if (string.IsNullOrWhiteSpace(lm.Message)) + { + context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMessage, ma.GetLocation())); + } + + var exSymbol = compilation.GetTypeByMetadataName("System.Exception"); + + foreach (var p in method.ParameterList.Parameters) + { + bool isExceptionType = false; + + var sm = compilation.GetSemanticModel(p.SyntaxTree); + if (sm != null) + { + var ct = sm.GetTypeInfo(p).ConvertedType; + if (ct != null) + { + var bt = ct.BaseType; + while (bt != null) + { + if (SymbolEqualityComparer.Default.Equals(bt, exSymbol)) + { + isExceptionType = true; + break; + } + bt = bt.BaseType; + } + } + } + + var lp = new LoggerParameter + { + Name = p.Identifier.ToString(), + Type = p.Type!.ToString(), + IsExceptionType = isExceptionType, + }; + lm.Parameters.Add(lp); + + if (lp.Name.StartsWith("__", StringComparison.Ordinal)) + { + // can't have logging method parameter names that start with __ since that can lead to conflicting symbol names + // because all generated symbols start with __ + context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidParameterName, p.Identifier.GetLocation())); + } + } + } + } + } + } + + yield return lc; + } + } + } + } + } + + private static bool HasTemplates(string message) + { + for (int i = 0; i < message.Length; i++) + { + var ch = message[i]; + if (ch == '{') + { + if (i < message.Length - 1 && message[i + 1] != '{') + { + // look for a non-escaped } + i++; + for (; i < message.Length; i++) + { + ch = message[i]; + if (ch == '}') + { + if (i == message.Length - 1 || message[i + 1] != '}') + { + return true; + } + } + } + + return false; + } + } + } + + return false; + } + +#pragma warning disable SA1401 // Fields should be private + + // An logging class holding a bunch of log methods + private class LoggerClass + { + public string? Namespace; + public string Name = string.Empty; + public string OriginalInterfaceName = string.Empty; + public List Methods = new(); + } + + // A log method in a logging class + private class LoggerMethod + { + public string Name = string.Empty; + public string Message = string.Empty; + public string Level = string.Empty; + public string EventId = string.Empty; + public List Parameters = new(); + public bool MessageHasTemplates; + } + + // A single parameter to a log method + private class LoggerParameter + { + public string Name = string.Empty; + public string Type = string.Empty; + public bool IsExceptionType; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.cs new file mode 100644 index 0000000000000..0a7050fa44ca0 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.cs @@ -0,0 +1,281 @@ +// © Microsoft Corporation. All rights reserved. + +namespace LoggingGen +{ + using System.Reflection.Metadata; + using System.Text; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Text; + + [Generator] + public partial class LoggingGenerator : ISourceGenerator + { + /// + public void Initialize(GeneratorInitializationContext context) + { + // No initialization required for this one + } + + /// + public void Execute(GeneratorExecutionContext context) + { + var types = new StringBuilder(); + foreach (var lc in GetLogClasses(context, context.Compilation)) + { + types.Append(GenType(lc)); + } + + var final = $@" +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; + +{types}"; + + context.AddSource(nameof(LoggingGenerator), SourceText.From(final, Encoding.UTF8)); + } + + private static string GenType(LoggerClass lc) + { + var methods = new StringBuilder(); + foreach (var lm in lc.Methods) + { + methods.Append(GenStruct(lm)); + methods.Append(GenEventId(lm)); + methods.Append(GenExtensionLogMethod(lm)); + } + + var namespaceStart = string.Empty; + var namespaceEnd = string.Empty; + + if (lc.Namespace != null) + { + namespaceStart = $"namespace {lc.Namespace}\n{{\n"; + namespaceEnd = "}\n"; + } + + return $@" +{namespaceStart} + static class {lc.Name} + {{ + {methods} + public static {lc.OriginalInterfaceName} Wrap(this ILogger logger) => new __Wrapper__(logger); + {GenWrapper(lc)} + }} +{namespaceEnd} +"; + } + + private static string GenWrapper(LoggerClass lc) + { + var methods = new StringBuilder(); + foreach (var lm in lc.Methods) + { + methods.Append(GenInstanceLogMethod(lm)); + } + + return $@" + private sealed class __Wrapper__ : {lc.OriginalInterfaceName} + {{ + private readonly ILogger __logger; + + public __Wrapper__(ILogger logger) => __logger = logger; + {methods} + }} +"; + } + + private static string GenStruct(LoggerMethod lm) + { + var constructor = string.Empty; + if (lm.Parameters.Count > 0) + { + constructor = $@" + public __{lm.Name}Struct__({GenParameters(lm)}) + {{ +{GenFieldAssignments(lm)} + }} +"; + } + + return $@" + private readonly struct __{lm.Name}Struct__ : IReadOnlyList> + {{ +{GenFields(lm)} +{constructor} + + public {(lm.MessageHasTemplates ? string.Empty : "static")} string Format() => $""{lm.Message}""; + + public int Count => {lm.Parameters.Count}; + + public KeyValuePair this[int index] + {{ + get + {{ + switch (index) + {{ +{GenCases(lm)} + default: + throw new ArgumentOutOfRangeException(nameof(index)); + }} + }} + }} + + public IEnumerator> GetEnumerator() + {{ +{GenEnumerator(lm)} + }} + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + }} +"; + } + + private static string GenEventId(LoggerMethod lm) + { + return $" private static readonly EventId __{lm.Name}EventId__ = new({lm.EventId}, nameof({lm.Name}));\n"; + } + + private static string GenInstanceLogMethod(LoggerMethod lm) + { + return $@" + public void {lm.Name}({GenParameters(lm)}) => __logger.{lm.Name}({GenArguments(lm)}); +"; + } + + private static string GenExtensionLogMethod(LoggerMethod lm) + { + string exceptionArg = "null"; + foreach (var p in lm.Parameters) + { + if (p.IsExceptionType) + { + exceptionArg = p.Name; + break; + } + } + + return $@" + public static void {lm.Name}(this ILogger logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {{ + if (logger.IsEnabled((LogLevel){lm.Level})) + {{ + var s = new __{lm.Name}Struct__({GenArguments(lm)}); + logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, s, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s." : "__" + lm.Name + "Struct__.")}Format()); + }} + }} +"; + } + + private static string GenParameters(LoggerMethod lm) + { + if (lm.Parameters.Count == 0) + { + return string.Empty; + } + + var sb = new StringBuilder(); + foreach (var p in lm.Parameters) + { + if (sb.Length > 0) + { + sb.Append(", "); + } + + sb.Append(p.Type); + sb.Append(' '); + sb.Append(p.Name); + } + + return sb.ToString(); + } + + private static string GenArguments(LoggerMethod lm) + { + if (lm.Parameters.Count == 0) + { + return string.Empty; + } + + var sb = new StringBuilder(); + foreach (var p in lm.Parameters) + { + if (sb.Length > 0) + { + sb.Append(", "); + } + + sb.Append(p.Name); + } + + return sb.ToString(); + } + + private static string GenFields(LoggerMethod lm) + { + if (lm.Parameters.Count == 0) + { + return string.Empty; + } + + var sb = new StringBuilder(); + foreach (var p in lm.Parameters) + { + sb.Append($" private readonly {p.Type} {p.Name};\n"); + } + + return sb.ToString(); + } + + private static string GenFieldAssignments(LoggerMethod lm) + { + if (lm.Parameters.Count == 0) + { + return string.Empty; + } + + var sb = new StringBuilder(); + foreach (var p in lm.Parameters) + { + sb.Append($" this.{p.Name} = {p.Name};\n"); + } + + return sb.ToString(); + } + + private static string GenEnumerator(LoggerMethod lm) + { + if (lm.Parameters.Count == 0) + { + return " yield break;\n"; + } + + var sb = new StringBuilder(); + foreach (var p in lm.Parameters) + { + sb.Append($" yield return new KeyValuePair(nameof({p.Name}), {p.Name});\n"); + } + + return sb.ToString(); + } + + private static string GenCases(LoggerMethod lm) + { + if (lm.Parameters.Count == 0) + { + return string.Empty; + } + + var sb = new StringBuilder(); + var index = 0; + foreach (var p in lm.Parameters) + { + sb.Append($" case {index++}:\n"); + sb.Append($" return new KeyValuePair(nameof({p.Name}), {p.Name});\n"); + } + + return sb.ToString(); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj new file mode 100644 index 0000000000000..6bfaf04ac99e7 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + 9.0 + enable + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs new file mode 100644 index 0000000000000..0301727181808 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs @@ -0,0 +1,258 @@ +// © Microsoft Corporation. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; +using NuGet.Frameworks; +using R9Logging; +using Xunit; + +// Used to test interfaces outside of a namespace +[LoggerExtensions] +interface ILoggerExtensionsNoNamespace +{ + [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + void CouldNotOpenSocketNoNamespace(string hostName); +} + +namespace R9Logging.Tests +{ + // used to test interfaces inside a namespace + [LoggerExtensions] + interface ILoggerExtensions + { + [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + void CouldNotOpenSocket(string hostName); + } + + [LoggerExtensions] + interface IArgTestExtensions + { + [LoggerMessage(0, LogLevel.Error, "M1")] + void Method1(); + + [LoggerMessage(1, LogLevel.Error, "M2")] + void Method2(string p1); + + [LoggerMessage(2, LogLevel.Error, "M3 {p1} {p2}")] + void Method3(string p1, int p2); + + [LoggerMessage(3, LogLevel.Error, "M4")] + void Method4(InvalidOperationException p1); + + [LoggerMessage(4, LogLevel.Error, "M5")] + void Method5(InvalidOperationException p1, InvalidOperationException p2); + + [LoggerMessage(5, LogLevel.Error, "M6")] + void Method6(InvalidOperationException p1, int p2); + + [LoggerMessage(6, LogLevel.Error, "M7")] + void Method7(int p1, InvalidOperationException p2); + } + + public class LoggingTests + { + [Fact] + public void ExtensionMethodTest() + { + var logger = new MockLogger(); + + logger.Reset(); + logger.CouldNotOpenSocket("microsoft.com"); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Null(logger.LastException); + Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + logger.CouldNotOpenSocketNoNamespace("microsoft.com"); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Null(logger.LastException); + Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + // same as above, but this time with explicit type references rather than extension method syntax so we can vouch for the namespaces being used + + logger.Reset(); + global::R9Logging.Tests.LoggerExtensions.CouldNotOpenSocket(logger, "microsoft.com"); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Null(logger.LastException); + Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + global::LoggerExtensionsNoNamespace.CouldNotOpenSocketNoNamespace(logger, "microsoft.com"); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Null(logger.LastException); + Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + } + + [Fact] + public void WrapperTypeTest() + { + var logger = new MockLogger(); + + logger.Reset(); + var d = global::R9Logging.Tests.LoggerExtensions.Wrap(logger); // make sure this is using the right namespace + d.CouldNotOpenSocket("microsoft.com"); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Null(logger.LastException); + Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + var d2 = global::LoggerExtensionsNoNamespace.Wrap(logger); // make sure this is outside of any namespace + d2.CouldNotOpenSocketNoNamespace("microsoft.com"); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Null(logger.LastException); + Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + } + + [Fact] + public void EnableTest() + { + var logger = new MockLogger(); + + logger.Reset(); + logger.Enabled = false; + var d = LoggerExtensions.Wrap(logger); + d.CouldNotOpenSocket("microsoft.com"); + Assert.Equal(0, logger.CallCount); // ensure the logger doesn't get called when it is disabled + + logger.CouldNotOpenSocket("microsoft.com"); + Assert.Equal(0, logger.CallCount); // ensure the logger doesn't get called when it is disabled + } + + [Fact] + public void ArgTest() + { + var logger = new MockLogger(); + var d = ArgTestExtensions.Wrap(logger); + + logger.Reset(); + d.Method1(); + Assert.Null(logger.LastException); + Assert.Equal("M1", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + d.Method2("arg1"); + Assert.Null(logger.LastException); + Assert.Equal("M2", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + d.Method3("arg1", 2); + Assert.Null(logger.LastException); + Assert.Equal("M3 arg1 2", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + d.Method4(new InvalidOperationException("A")); +// Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("M4", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + d.Method5(new InvalidOperationException("A"), new InvalidOperationException("B")); +// Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("M5", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + d.Method6(new InvalidOperationException("A"), 2); +// Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("M6", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + d.Method7(1, new InvalidOperationException("B")); +// Assert.Equal("B", logger.LastException!.Message); + Assert.Equal("M7", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method1(logger); + Assert.Null(logger.LastException); + Assert.Equal("M1", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method2(logger, "arg1"); + Assert.Null(logger.LastException); + Assert.Equal("M2", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method3(logger, "arg1", 2); + Assert.Null(logger.LastException); + Assert.Equal("M3 arg1 2", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method4(logger, new InvalidOperationException("A")); + // Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("M4", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method5(logger, new InvalidOperationException("A"), new InvalidOperationException("B")); + // Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("M5", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method6(logger, new InvalidOperationException("A"), 2); + // Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("M6", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method7(logger, 1, new InvalidOperationException("B")); + // Assert.Equal("B", logger.LastException!.Message); + Assert.Equal("M7", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + } + + [Fact] + public void ReadOnlyListTest() + { + var logger = new MockLogger(); + var d = ArgTestExtensions.Wrap(logger); + + logger.Reset(); + d.Method1(); + var rol = logger.LastState as IReadOnlyList>; + Assert.Equal(0, rol!.Count); + Assert.Empty(rol); + Assert.Throws(() => _ = rol[0]); + + logger.Reset(); + d.Method2("arg1"); + rol = logger.LastState as IReadOnlyList>; + Assert.Equal(1, rol!.Count); +#pragma warning disable CA1829 // Use Length/Count property instead of Count() when available + Assert.Equal(1, rol.LongCount()); +#pragma warning restore CA1829 // Use Length/Count property instead of Count() when available + Assert.Equal("p1", (string)rol[0].Key); + Assert.Equal("arg1", (string)rol[0].Value); + Assert.Throws(() => _ = rol[1]); + + logger.Reset(); + d.Method3("arg1", 2); + rol = logger.LastState as IReadOnlyList>; + Assert.Equal(2, rol!.Count); +#pragma warning disable CA1829 // Use Length/Count property instead of Count() when available + Assert.Equal(2, rol.LongCount()); +#pragma warning restore CA1829 // Use Length/Count property instead of Count() when available + Assert.Equal("p1", (string)rol[0].Key); + Assert.Equal("arg1", (string)rol[0].Value); + Assert.Equal("p2", (string)rol[1].Key); + Assert.Equal(2, (int)rol[1].Value); + Assert.Throws(() => _ = rol[2]); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj new file mode 100644 index 0000000000000..6f568e02daf73 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -0,0 +1,29 @@ + + + + net5.0 + false + enable + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs new file mode 100644 index 0000000000000..6c166aeb97c2a --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -0,0 +1,67 @@ +// © Microsoft Corporation. All rights reserved. + +using System; +using Microsoft.Extensions.Logging; + +namespace R9Logging.Tests +{ + /// + /// A logger which captures the last log state logged to it. + /// + class MockLogger : ILogger + { + public LogLevel LastLogLevel { get; private set; } + public EventId LastEventId { get; private set; } + public object? LastState { get; private set; } + public Exception? LastException { get; private set; } + public string? LastFormattedString { get; private set; } + public bool Enabled { get; set; } + public int CallCount { get; private set; } + + /// + /// Dummy disposable type, for use with BeginScope + /// + class Disposable : IDisposable + { + public void Dispose() + { + } + } + + public MockLogger() + { + Reset(); + } + + public IDisposable BeginScope(TState state) + { + return new Disposable(); + } + + public bool IsEnabled(LogLevel logLevel) + { + return Enabled; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + LastLogLevel = logLevel; + LastEventId = eventId; + LastState = state; + LastException = exception; + LastFormattedString = formatter(state, exception); + CallCount++; + } + + public void Reset() + { + LastLogLevel = (LogLevel)(-1); + LastEventId = new EventId(); + LastState = null; + LastException = null; + LastFormattedString = null; + CallCount = 0; + Enabled = true; + } + } +} From afd85ab2ea6149b2aaa5ad28455ea09452f2820a Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 14:11:11 -0800 Subject: [PATCH 002/121] Update namespaces --- ...LoggingGen.Parse.cs => LoggingGenerator.Parse.cs} | 12 ++++++------ .../gen/{LoggingGen.cs => LoggingGenerator.cs} | 2 +- .../LoggingTests.cs | 9 ++++----- ...rosoft.Extensions.Logging.Generators.Tests.csproj | 8 ++++++-- .../MockLogger.cs | 3 +-- 5 files changed, 18 insertions(+), 16 deletions(-) rename src/libraries/Microsoft.Extensions.Logging/gen/{LoggingGen.Parse.cs => LoggingGenerator.Parse.cs} (96%) rename src/libraries/Microsoft.Extensions.Logging/gen/{LoggingGen.cs => LoggingGenerator.cs} (99%) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs similarity index 96% rename from src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.Parse.cs rename to src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 16f1fc4d33f80..728ad63020aaf 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -1,6 +1,6 @@ // © Microsoft Corporation. All rights reserved. -namespace LoggingGen +namespace Microsoft.Extensions.Logging.Generators { using System; using System.Collections.Generic; @@ -11,7 +11,7 @@ namespace LoggingGen public partial class LoggingGenerator { - private const string DiagnosticCategory = "R9LoggingGenerator"; + private const string DiagnosticCategory = "LoggingGenerator"; #pragma warning disable RS2008 // Enable analyzer release tracking @@ -71,19 +71,19 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); var allInterfaces = allNodes.Where(d => d.IsKind(SyntaxKind.InterfaceDeclaration)).OfType(); - var logExtensionsAttribute = compilation.GetTypeByMetadataName("R9Logging.LoggerExtensionsAttribute"); + var logExtensionsAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.Attributes.LoggerExtensionsAttribute"); if (logExtensionsAttribute is null) { // emit a diagnostic about the attribute not being present but needing to be - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "R9Logging.LoggerExtensionsAttribute")); + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "Microsoft.Extensions.Logging.Attributes.LoggerExtensionsAttribute")); yield break; } - var loggerMessageAttribute = compilation.GetTypeByMetadataName("R9Logging.LoggerMessageAttribute"); + var loggerMessageAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.Attributes.LoggerMessageAttribute"); if (loggerMessageAttribute is null) { // emit a diagnostic about the attribute not being present but needing to be - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "R9Logging.LoggerMessageAttribute")); + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "Microsoft.Extensions.Logging.Attributes.LoggerMessageAttribute")); yield break; } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs similarity index 99% rename from src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.cs rename to src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 0a7050fa44ca0..ce442c448df80 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGen.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -1,6 +1,6 @@ // © Microsoft Corporation. All rights reserved. -namespace LoggingGen +namespace Microsoft.Extensions.Logging.Generators { using System.Reflection.Metadata; using System.Text; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs index 0301727181808..f5cc98e73b4d7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs @@ -4,8 +4,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; -using NuGet.Frameworks; -using R9Logging; +using Microsoft.Extensions.Logging.Attributes; using Xunit; // Used to test interfaces outside of a namespace @@ -16,7 +15,7 @@ interface ILoggerExtensionsNoNamespace void CouldNotOpenSocketNoNamespace(string hostName); } -namespace R9Logging.Tests +namespace Microsoft.Extensions.Logging.Generators.Tests { // used to test interfaces inside a namespace [LoggerExtensions] @@ -75,7 +74,7 @@ public void ExtensionMethodTest() // same as above, but this time with explicit type references rather than extension method syntax so we can vouch for the namespaces being used logger.Reset(); - global::R9Logging.Tests.LoggerExtensions.CouldNotOpenSocket(logger, "microsoft.com"); + global::Microsoft.Extensions.Logging.Generators.Tests.LoggerExtensions.CouldNotOpenSocket(logger, "microsoft.com"); Assert.Equal(LogLevel.Critical, logger.LastLogLevel); Assert.Null(logger.LastException); Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); @@ -95,7 +94,7 @@ public void WrapperTypeTest() var logger = new MockLogger(); logger.Reset(); - var d = global::R9Logging.Tests.LoggerExtensions.Wrap(logger); // make sure this is using the right namespace + var d = global::Microsoft.Extensions.Logging.Generators.Tests.LoggerExtensions.Wrap(logger); // make sure this is using the right namespace d.CouldNotOpenSocket("microsoft.com"); Assert.Equal(LogLevel.Critical, logger.LastLogLevel); Assert.Null(logger.LastException); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 6f568e02daf73..feb5633d68857 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -22,8 +22,12 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + + + + diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs index 6c166aeb97c2a..b5383e0ac30a4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -1,9 +1,8 @@ // © Microsoft Corporation. All rights reserved. using System; -using Microsoft.Extensions.Logging; -namespace R9Logging.Tests +namespace Microsoft.Extensions.Logging.Generators.Tests { /// /// A logger which captures the last log state logged to it. From fe21a95101792daa49bba34a0f5ee9a1e193a3c5 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 14:14:12 -0800 Subject: [PATCH 003/121] Nuke the temporary .Attributes namespace --- .../gen/LoggingGenerator.Parse.cs | 8 ++++---- .../LoggingTests.cs | 1 - .../MockLogger.cs | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 728ad63020aaf..0f3ca23db1c8d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -71,19 +71,19 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); var allInterfaces = allNodes.Where(d => d.IsKind(SyntaxKind.InterfaceDeclaration)).OfType(); - var logExtensionsAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.Attributes.LoggerExtensionsAttribute"); + var logExtensionsAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); if (logExtensionsAttribute is null) { // emit a diagnostic about the attribute not being present but needing to be - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "Microsoft.Extensions.Logging.Attributes.LoggerExtensionsAttribute")); + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "Microsoft.Extensions.Logging.LoggerExtensionsAttribute")); yield break; } - var loggerMessageAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.Attributes.LoggerMessageAttribute"); + var loggerMessageAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); if (loggerMessageAttribute is null) { // emit a diagnostic about the attribute not being present but needing to be - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "Microsoft.Extensions.Logging.Attributes.LoggerMessageAttribute")); + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute")); yield break; } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs index f5cc98e73b4d7..abdb37fc061a1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Attributes; using Xunit; // Used to test interfaces outside of a namespace diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs index b5383e0ac30a4..aae161333e114 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -1,9 +1,9 @@ // © Microsoft Corporation. All rights reserved. -using System; - namespace Microsoft.Extensions.Logging.Generators.Tests { + using System; + /// /// A logger which captures the last log state logged to it. /// From b6281b5576a6279429ac2014644907ee39cde25a Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 14:33:14 -0800 Subject: [PATCH 004/121] Add error checking to prevent multiple logging messages from using the same event id --- .../gen/LoggingGenerator.Parse.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 0f3ca23db1c8d..3871da5aca6d1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -63,6 +63,14 @@ public partial class LoggingGenerator DiagnosticSeverity.Error, isEnabledByDefault: true); + private static readonly DiagnosticDescriptor ErrorEventIdReuse = new( + id: "LG6", + title: "Multiple logging messages cannot use the same event id", + messageFormat: "Multiple logging messages are using event id {0}", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + /// /// Gets the known set of annotated logger classes /// @@ -190,6 +198,14 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodName, method.Identifier.GetLocation())); } + foreach (var m in lc.Methods) + { + if (m != lm && m.EventId == lm.EventId) + { + context.ReportDiagnostic(Diagnostic.Create(ErrorEventIdReuse, ma.ArgumentList!.Arguments[0].GetLocation(), m.EventId)); + } + } + if (string.IsNullOrWhiteSpace(lm.Message)) { context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMessage, ma.GetLocation())); From ca3ead4add1e8769e3913eb5a6a3ea840e8cc67b Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Tue, 17 Nov 2020 14:48:35 -0800 Subject: [PATCH 005/121] Use ISyntaxReceiver to be more IDE friendly The `ISyntaxReceiver` is a syntax model that is more IDE friendly than the pull model that was previously implemented. This allows the IDE to push data to the generator as it is processing it. This is important because the IDE is constantly "abandoning" analysis as the customer types in the IDE which invalidates state and they quickly want to move to calculating the new state --- .../gen/LoggingGenerator.Parse.cs | 6 +++--- .../gen/LoggingGenerator.cs | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 3871da5aca6d1..01a48b871a6de 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -76,8 +76,8 @@ public partial class LoggingGenerator /// private static IEnumerable GetLogClasses(GeneratorExecutionContext context, Compilation compilation) { - var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); - var allInterfaces = allNodes.Where(d => d.IsKind(SyntaxKind.InterfaceDeclaration)).OfType(); + if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) + yield break; var logExtensionsAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); if (logExtensionsAttribute is null) @@ -98,7 +98,7 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext // Temp work around for https://github.com/dotnet/roslyn/pull/49330 var semanticModelMap = new Dictionary(); - foreach (var iface in allInterfaces) + foreach (var iface in receiver.InterfaceDeclarations) { foreach (var al in iface.AttributeLists) { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index ce442c448df80..ae2e205f71627 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -2,9 +2,11 @@ namespace Microsoft.Extensions.Logging.Generators { + using System.Collections.Generic; using System.Reflection.Metadata; using System.Text; using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; [Generator] @@ -13,7 +15,7 @@ public partial class LoggingGenerator : ISourceGenerator /// public void Initialize(GeneratorInitializationContext context) { - // No initialization required for this one + context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); } /// @@ -277,5 +279,18 @@ private static string GenCases(LoggerMethod lm) return sb.ToString(); } + + private sealed class SyntaxReceiver : ISyntaxReceiver + { + public List InterfaceDeclarations { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is InterfaceDeclarationSyntax interfaceSyntax && interfaceSyntax.AttributeLists.Count > 0) + { + InterfaceDeclarations.Add(interfaceSyntax); + } + } + } } } From 470b1601ffbe4899dddd1f5e19357d73c48e8a0f Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 15:07:46 -0800 Subject: [PATCH 006/121] Finish implementation of exception support --- .../gen/LoggingGenerator.Parse.cs | 37 +++++++------------ .../LoggingTests.cs | 8 ++-- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 01a48b871a6de..88ded95c766a7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -95,6 +95,12 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext yield break; } + var exSymbol = compilation.GetTypeByMetadataName("System.Exception"); + if (exSymbol == null) + { + yield break; + } + // Temp work around for https://github.com/dotnet/roslyn/pull/49330 var semanticModelMap = new Dictionary(); @@ -211,36 +217,15 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMessage, ma.GetLocation())); } - var exSymbol = compilation.GetTypeByMetadataName("System.Exception"); - foreach (var p in method.ParameterList.Parameters) { - bool isExceptionType = false; - - var sm = compilation.GetSemanticModel(p.SyntaxTree); - if (sm != null) - { - var ct = sm.GetTypeInfo(p).ConvertedType; - if (ct != null) - { - var bt = ct.BaseType; - while (bt != null) - { - if (SymbolEqualityComparer.Default.Equals(bt, exSymbol)) - { - isExceptionType = true; - break; - } - bt = bt.BaseType; - } - } - } + var pSymbol = compilation.GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; var lp = new LoggerParameter { Name = p.Identifier.ToString(), Type = p.Type!.ToString(), - IsExceptionType = isExceptionType, + IsExceptionType = IsBaseOrIdentity(compilation, pSymbol, exSymbol), }; lm.Parameters.Add(lp); @@ -263,6 +248,12 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext } } + static bool IsBaseOrIdentity(Compilation compilation, ITypeSymbol source, ITypeSymbol dest) + { + var conversion = compilation.ClassifyConversion(source, dest); + return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit); + } + private static bool HasTemplates(string message) { for (int i = 0; i < message.Length; i++) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs index abdb37fc061a1..2ff8dc516c877 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs @@ -192,25 +192,25 @@ public void ArgTest() logger.Reset(); ArgTestExtensions.Method4(logger, new InvalidOperationException("A")); - // Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("A", logger.LastException!.Message); Assert.Equal("M4", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); ArgTestExtensions.Method5(logger, new InvalidOperationException("A"), new InvalidOperationException("B")); - // Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("A", logger.LastException!.Message); Assert.Equal("M5", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); ArgTestExtensions.Method6(logger, new InvalidOperationException("A"), 2); - // Assert.Equal("A", logger.LastException!.Message); + Assert.Equal("A", logger.LastException!.Message); Assert.Equal("M6", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); ArgTestExtensions.Method7(logger, 1, new InvalidOperationException("B")); - // Assert.Equal("B", logger.LastException!.Message); + Assert.Equal("B", logger.LastException!.Message); Assert.Equal("M7", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); } From 515f029ea59393e1a9f568ab24bd970688a682e0 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 15:41:18 -0800 Subject: [PATCH 007/121] Make the generated type have the same access modifiers as the input interface type --- .../Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs | 2 ++ .../Microsoft.Extensions.Logging/gen/LoggingGenerator.cs | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 88ded95c766a7..0f875c54da0d6 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -156,6 +156,7 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext Namespace = ns?.Name.ToString(), Name = name, OriginalInterfaceName = iface.Identifier.ToString(), + AccessModifiers = iface.Modifiers.ToString(), }; if (string.IsNullOrWhiteSpace(lc.Name)) @@ -293,6 +294,7 @@ private class LoggerClass public string? Namespace; public string Name = string.Empty; public string OriginalInterfaceName = string.Empty; + public string AccessModifiers = string.Empty; public List Methods = new(); } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index ae2e205f71627..66b67a2704fef 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -3,7 +3,6 @@ namespace Microsoft.Extensions.Logging.Generators { using System.Collections.Generic; - using System.Reflection.Metadata; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -59,7 +58,7 @@ private static string GenType(LoggerClass lc) return $@" {namespaceStart} - static class {lc.Name} + {lc.AccessModifiers} static class {lc.Name} {{ {methods} public static {lc.OriginalInterfaceName} Wrap(this ILogger logger) => new __Wrapper__(logger); From 6affdd8e6f700bf3fe85123e097641153c215288 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 15:58:37 -0800 Subject: [PATCH 008/121] Enforce that logging methods must return void --- .../gen/LoggingGenerator.Parse.cs | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 0f875c54da0d6..c76695ff4b0ca 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -5,6 +5,7 @@ namespace Microsoft.Extensions.Logging.Generators using System; using System.Collections.Generic; using System.Linq; + using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -55,10 +56,10 @@ public partial class LoggingGenerator DiagnosticSeverity.Error, isEnabledByDefault: true); - private static readonly DiagnosticDescriptor ErrorMissingAttributeType = new( + private static readonly DiagnosticDescriptor ErrorMissingRequiredType = new( id: "LG5", - title: "Could not find a required attribute definition", - messageFormat: "Could not find definition for attribute {0}", + title: "Could not find a required type definition", + messageFormat: "Could not find definition for type {0}", category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -71,33 +72,49 @@ public partial class LoggingGenerator DiagnosticSeverity.Error, isEnabledByDefault: true); + private static readonly DiagnosticDescriptor ErrorInvalidMethodReturnType = new( + id: "LG7", + title: "Logging methods must return void", + messageFormat: "Logging methods must return void", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + /// /// Gets the known set of annotated logger classes /// private static IEnumerable GetLogClasses(GeneratorExecutionContext context, Compilation compilation) { if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) + { yield break; + } var logExtensionsAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); if (logExtensionsAttribute is null) { - // emit a diagnostic about the attribute not being present but needing to be - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "Microsoft.Extensions.Logging.LoggerExtensionsAttribute")); + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerExtensionsAttribute")); yield break; } var loggerMessageAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); if (loggerMessageAttribute is null) { - // emit a diagnostic about the attribute not being present but needing to be - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingAttributeType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute")); + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute")); yield break; } var exSymbol = compilation.GetTypeByMetadataName("System.Exception"); if (exSymbol == null) { + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingRequiredType, null, "System.Exception")); + yield break; + } + + var voidSymbol = compilation.GetTypeByMetadataName("System.Void"); + if (voidSymbol == null) + { + context.ReportDiagnostic(Diagnostic.Create(ErrorMissingRequiredType, null, "System.Void")); yield break; } @@ -205,6 +222,11 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodName, method.Identifier.GetLocation())); } + if (!compilation.GetSemanticModel(method.ReturnType.SyntaxTree).GetTypeInfo(method.ReturnType!).Type!.Equals(voidSymbol, SymbolEqualityComparer.Default)) + { + context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodReturnType, method.ReturnType.GetLocation())); + } + foreach (var m in lc.Methods) { if (m != lm && m.EventId == lm.EventId) From 7f15797992e1c04b36b83d203578693eeaf71717 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 16:28:01 -0800 Subject: [PATCH 009/121] Cleanup how semantic models are handled --- .../gen/LoggingGenerator.Parse.cs | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index c76695ff4b0ca..0ee3445e0ec67 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -118,7 +118,6 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext yield break; } - // Temp work around for https://github.com/dotnet/roslyn/pull/49330 var semanticModelMap = new Dictionary(); foreach (var iface in receiver.InterfaceDeclarations) @@ -127,16 +126,12 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext { foreach (var a in al.Attributes) { - if (!semanticModelMap.TryGetValue(a.SyntaxTree, out var semanticModel)) - { - semanticModel = compilation.GetSemanticModel(a.SyntaxTree); - semanticModelMap[a.SyntaxTree] = semanticModel; - } - // does this interface have the [LoggerExtensions] atribute? + var semanticModel = GetSemanticModel(compilation, semanticModelMap, a.SyntaxTree); var aSymbol = semanticModel.GetSymbolInfo(a, context.CancellationToken); if (aSymbol.Symbol is IMethodSymbol methodSymbol && logExtensionsAttribute.Equals(methodSymbol.ContainingType, SymbolEqualityComparer.Default)) { + // determine the namespace the interface is declared in, if any NamespaceDeclarationSyntax? ns = null; if (iface.Parent != null) { @@ -148,11 +143,13 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext } } + // determine the name of the generated type + string? name = null; if (a.ArgumentList?.Arguments.Count > 0) { var arg = a.ArgumentList!.Arguments[0]; - name = compilation.GetSemanticModel(a.SyntaxTree).GetConstantValue(arg.Expression).ToString(); + name = semanticModel.GetConstantValue(arg.Expression).ToString(); } if (name == null) @@ -181,29 +178,25 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidTypeName, a.GetLocation())); } + var ids = new HashSet(); foreach (var method in iface.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) { foreach (var mal in method.AttributeLists) { foreach (var ma in mal.Attributes) { - if (!semanticModelMap.TryGetValue(ma.SyntaxTree, out semanticModel)) - { - semanticModel = compilation.GetSemanticModel(ma.SyntaxTree); - semanticModelMap[ma.SyntaxTree] = semanticModel; - } - + semanticModel = GetSemanticModel(compilation, semanticModelMap, ma.SyntaxTree); var maSymbol = semanticModel.GetSymbolInfo(ma, context.CancellationToken); if (maSymbol.Symbol is IMethodSymbol ms && loggerMessageAttribute.Equals(ms.ContainingType, SymbolEqualityComparer.Default)) { var arg = ma.ArgumentList!.Arguments[0]; - var eventId = compilation.GetSemanticModel(ma.SyntaxTree).GetConstantValue(arg.Expression).ToString(); + var eventId = semanticModel.GetConstantValue(arg.Expression).ToString(); arg = ma.ArgumentList!.Arguments[1]; - var level = compilation.GetSemanticModel(ma.SyntaxTree).GetConstantValue(arg.Expression).ToString(); + var level = semanticModel.GetConstantValue(arg.Expression).ToString(); arg = ma.ArgumentList!.Arguments[2]; - var message = compilation.GetSemanticModel(ma.SyntaxTree).GetConstantValue(arg.Expression).ToString(); + var message = semanticModel.GetConstantValue(arg.Expression).ToString(); var lm = new LoggerMethod { @@ -222,17 +215,19 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodName, method.Identifier.GetLocation())); } - if (!compilation.GetSemanticModel(method.ReturnType.SyntaxTree).GetTypeInfo(method.ReturnType!).Type!.Equals(voidSymbol, SymbolEqualityComparer.Default)) + if (!GetSemanticModel(compilation, semanticModelMap, method.ReturnType.SyntaxTree).GetTypeInfo(method.ReturnType!).Type!.Equals(voidSymbol, SymbolEqualityComparer.Default)) { context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodReturnType, method.ReturnType.GetLocation())); } - foreach (var m in lc.Methods) + // ensure there are no duplicate ids. + if (ids.Contains(lm.EventId)) { - if (m != lm && m.EventId == lm.EventId) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorEventIdReuse, ma.ArgumentList!.Arguments[0].GetLocation(), m.EventId)); - } + context.ReportDiagnostic(Diagnostic.Create(ErrorEventIdReuse, ma.ArgumentList!.Arguments[0].GetLocation(), lm.EventId)); + } + else + { + ids.Add(lm.EventId); } if (string.IsNullOrWhiteSpace(lm.Message)) @@ -242,7 +237,7 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext foreach (var p in method.ParameterList.Parameters) { - var pSymbol = compilation.GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; + var pSymbol = GetSemanticModel(compilation, semanticModelMap, p.SyntaxTree).GetTypeInfo(p.Type!).Type!; var lp = new LoggerParameter { @@ -271,6 +266,18 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext } } + // Workaround for https://github.com/dotnet/roslyn/pull/49330 + static SemanticModel GetSemanticModel(Compilation compilation, Dictionary semanticModelMap, SyntaxTree syntaxTree) + { + if (!semanticModelMap.TryGetValue(syntaxTree, out var semanticModel)) + { + semanticModel = compilation.GetSemanticModel(syntaxTree); + semanticModelMap[syntaxTree] = semanticModel; + } + + return semanticModel; + } + static bool IsBaseOrIdentity(Compilation compilation, ITypeSymbol source, ITypeSymbol dest) { var conversion = compilation.ClassifyConversion(source, dest); From 8b3cdaae5155a01cc91242e650ecad32a28581ab Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 17 Nov 2020 16:55:20 -0800 Subject: [PATCH 010/121] Prevent generic interfaces or methods. --- .../gen/LoggingGenerator.Parse.cs | 30 +++++++++++++++++++ .../gen/LoggingGenerator.cs | 2 ++ 2 files changed, 32 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 0ee3445e0ec67..494898ff5fe99 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -80,6 +80,22 @@ public partial class LoggingGenerator DiagnosticSeverity.Error, isEnabledByDefault: true); + private static readonly DiagnosticDescriptor ErrorInterfaceGeneric = new( + id: "LG8", + title: "Logging interfaces cannot be generic", + messageFormat: "Logging interfaces cannot be generic", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorMethodGeneric = new( + id: "LG9", + title: "Logging methods cannot be generic", + messageFormat: "Logging methods cannot be generic", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + /// /// Gets the known set of annotated logger classes /// @@ -173,11 +189,18 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext AccessModifiers = iface.Modifiers.ToString(), }; +// lc.Documentation = a.GetLeadingTrivia().ToString(); + if (string.IsNullOrWhiteSpace(lc.Name)) { context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidTypeName, a.GetLocation())); } + if (iface.Arity > 0) + { + context.ReportDiagnostic(Diagnostic.Create(ErrorInterfaceGeneric, iface.GetLocation())); + } + var ids = new HashSet(); foreach (var method in iface.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) { @@ -220,6 +243,11 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodReturnType, method.ReturnType.GetLocation())); } + if (method.Arity > 0) + { + context.ReportDiagnostic(Diagnostic.Create(ErrorMethodGeneric, iface.GetLocation())); + } + // ensure there are no duplicate ids. if (ids.Contains(lm.EventId)) { @@ -325,6 +353,7 @@ private class LoggerClass public string OriginalInterfaceName = string.Empty; public string AccessModifiers = string.Empty; public List Methods = new(); + public string Documentation = string.Empty; } // A log method in a logging class @@ -336,6 +365,7 @@ private class LoggerMethod public string EventId = string.Empty; public List Parameters = new(); public bool MessageHasTemplates; + public string Documentation = string.Empty; } // A single parameter to a log method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 66b67a2704fef..f7fa6eab23232 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -58,6 +58,7 @@ private static string GenType(LoggerClass lc) return $@" {namespaceStart} + {lc.Documentation} {lc.AccessModifiers} static class {lc.Name} {{ {methods} @@ -158,6 +159,7 @@ private static string GenExtensionLogMethod(LoggerMethod lm) } return $@" + {lm.Documentation} public static void {lm.Name}(this ILogger logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (logger.IsEnabled((LogLevel){lm.Level})) From 1c0e75631405ab1a36963f08a4431cc948f6ea53 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 18 Nov 2020 06:07:54 -0800 Subject: [PATCH 011/121] Improve efficiency of the generated code for log messages without templates --- .../gen/LoggingGenerator.Parse.cs | 18 +++++++++++++++--- .../gen/LoggingGenerator.cs | 14 +++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 494898ff5fe99..79e71afdd7f1b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -5,7 +5,6 @@ namespace Microsoft.Extensions.Logging.Generators using System; using System.Collections.Generic; using System.Linq; - using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -187,10 +186,9 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext Name = name, OriginalInterfaceName = iface.Identifier.ToString(), AccessModifiers = iface.Modifiers.ToString(), + Documentation = GetDocs(iface), }; -// lc.Documentation = a.GetLeadingTrivia().ToString(); - if (string.IsNullOrWhiteSpace(lc.Name)) { context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidTypeName, a.GetLocation())); @@ -228,6 +226,7 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext Level = level, Message = message, MessageHasTemplates = HasTemplates(message), + Documentation = GetDocs(method), }; lc.Methods.Add(lm); @@ -294,6 +293,19 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext } } + static string GetDocs(SyntaxNode node) + { + foreach (var trivia in node.GetLeadingTrivia().Where(x => x.HasStructure)) + { + if (trivia.GetStructure() is DocumentationCommentTriviaSyntax sTrivia) + { + return sTrivia.ToString(); + } + } + + return string.Empty; + } + // Workaround for https://github.com/dotnet/roslyn/pull/49330 static SemanticModel GetSemanticModel(Compilation compilation, Dictionary semanticModelMap, SyntaxTree syntaxTree) { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index f7fa6eab23232..bf1a520d1df00 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -2,6 +2,7 @@ namespace Microsoft.Extensions.Logging.Generators { + using System; using System.Collections.Generic; using System.Text; using Microsoft.CodeAnalysis; @@ -101,13 +102,20 @@ private static string GenStruct(LoggerMethod lm) "; } + var format = string.Empty; + if (lm.MessageHasTemplates) + { + format = $@" + public string Format() => $""{lm.Message}""; +"; + } + return $@" private readonly struct __{lm.Name}Struct__ : IReadOnlyList> {{ {GenFields(lm)} {constructor} - - public {(lm.MessageHasTemplates ? string.Empty : "static")} string Format() => $""{lm.Message}""; +{format} public int Count => {lm.Parameters.Count}; @@ -165,7 +173,7 @@ private static string GenExtensionLogMethod(LoggerMethod lm) if (logger.IsEnabled((LogLevel){lm.Level})) {{ var s = new __{lm.Name}Struct__({GenArguments(lm)}); - logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, s, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s." : "__" + lm.Name + "Struct__.")}Format()); + logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, s, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.Format()" : "\"" + lm.Message + "\"")}); }} }} "; From 58930904bdf32b674ca62c3fcaf69cc01282bc2d Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 18 Nov 2020 06:49:13 -0800 Subject: [PATCH 012/121] Various cleanup items --- .../gen/LoggingGenerator.Parse.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 79e71afdd7f1b..b17347070e5bb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -26,7 +26,7 @@ public partial class LoggingGenerator private static readonly DiagnosticDescriptor ErrorInvalidMessage = new( id: "LG1", title: "Missing message for logging method", - messageFormat: "Missing message for logging method", + messageFormat: "Missing message for logging method {0}", category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -41,8 +41,8 @@ public partial class LoggingGenerator private static readonly DiagnosticDescriptor ErrorInvalidTypeName = new( id: "LG3", - title: "Missing generated type name", - messageFormat: "Missing generated type name", + title: "Unable to automatically derive generated type name", + messageFormat: "Unable to automatically derive a generated type name based on the interface name of {0}, please specify an explicit generated type name instead", category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -96,12 +96,13 @@ public partial class LoggingGenerator isEnabledByDefault: true); /// - /// Gets the known set of annotated logger classes + /// Gets the set of logging classes that should be generated based on the discovered annotated interfaces. /// private static IEnumerable GetLogClasses(GeneratorExecutionContext context, Compilation compilation) { if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) { + // nothing to do yet yield break; } @@ -167,16 +168,19 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext name = semanticModel.GetConstantValue(arg.Expression).ToString(); } + // if no name was specified, try to derive it from the interface name + var ifaceName = iface.Identifier.ToString(); if (name == null) { - var ifaceName = iface.Identifier.ToString(); - if (ifaceName[0] == 'I' && ifaceName.Length > 1) + if (ifaceName[0] == 'I') { + // strip the leading I name = ifaceName.Substring(1); } else { - name = ifaceName + "Extensions"; + // fail + name = string.Empty; } } @@ -191,7 +195,7 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext if (string.IsNullOrWhiteSpace(lc.Name)) { - context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidTypeName, a.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidTypeName, a.GetLocation(), ifaceName)); } if (iface.Arity > 0) @@ -244,7 +248,7 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext if (method.Arity > 0) { - context.ReportDiagnostic(Diagnostic.Create(ErrorMethodGeneric, iface.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(ErrorMethodGeneric, method.GetLocation())); } // ensure there are no duplicate ids. @@ -259,7 +263,7 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext if (string.IsNullOrWhiteSpace(lm.Message)) { - context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMessage, ma.GetLocation())); + context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMessage, ma.GetLocation(), method.Identifier.ToString())); } foreach (var p in method.ParameterList.Parameters) @@ -293,8 +297,10 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext } } - static string GetDocs(SyntaxNode node) + static string GetDocs(SyntaxNode _) { +#if false +This is not ready yet foreach (var trivia in node.GetLeadingTrivia().Where(x => x.HasStructure)) { if (trivia.GetStructure() is DocumentationCommentTriviaSyntax sTrivia) @@ -302,7 +308,7 @@ static string GetDocs(SyntaxNode node) return sTrivia.ToString(); } } - +#endif return string.Empty; } From 56943dd220ee03d0504a809efbf987cb89c9a17c Mon Sep 17 00:00:00 2001 From: David Fowler Date: Wed, 18 Nov 2020 12:17:18 -0400 Subject: [PATCH 013/121] Few small changes (#3) - Use the logger factory and console logger in the sample - Support overriding the event name via the logger message attribute - Added ToString override - Enable dumping generated code in the sample for easy debugging --- .../gen/LoggingGenerator.Parse.cs | 12 +++++++++++- .../gen/LoggingGenerator.cs | 7 ++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index b17347070e5bb..3121027af4ddf 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -223,12 +223,21 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext arg = ma.ArgumentList!.Arguments[2]; var message = semanticModel.GetConstantValue(arg.Expression).ToString(); + string? eventName = null; + + if (ma.ArgumentList?.Arguments is { Count: > 3 } args) + { + arg = args[3]; + eventName = semanticModel.GetConstantValue(arg.Expression).ToString(); + } + var lm = new LoggerMethod { Name = method.Identifier.ToString(), EventId = eventId, Level = level, Message = message, + EventName = eventName, MessageHasTemplates = HasTemplates(message), Documentation = GetDocs(method), }; @@ -329,7 +338,7 @@ static bool IsBaseOrIdentity(Compilation compilation, ITypeSymbol source, ITypeS var conversion = compilation.ClassifyConversion(source, dest); return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit); } - + private static bool HasTemplates(string message) { for (int i = 0; i < message.Length; i++) @@ -381,6 +390,7 @@ private class LoggerMethod public string Message = string.Empty; public string Level = string.Empty; public string EventId = string.Empty; + public string? EventName = null!; public List Parameters = new(); public bool MessageHasTemplates; public string Documentation = string.Empty; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index bf1a520d1df00..88f5d240d137d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -107,6 +107,7 @@ private static string GenStruct(LoggerMethod lm) { format = $@" public string Format() => $""{lm.Message}""; + public override string ToString() => Format(); "; } @@ -144,7 +145,7 @@ public IEnumerator> GetEnumerator() private static string GenEventId(LoggerMethod lm) { - return $" private static readonly EventId __{lm.Name}EventId__ = new({lm.EventId}, nameof({lm.Name}));\n"; + return $" private static readonly EventId __{lm.Name}EventId__ = new({lm.EventId}, " + (lm.EventName is null ? $"nameof({lm.Name})" : $"\"{lm.EventName}\"") + ");\n"; } private static string GenInstanceLogMethod(LoggerMethod lm) @@ -172,8 +173,8 @@ private static string GenExtensionLogMethod(LoggerMethod lm) {{ if (logger.IsEnabled((LogLevel){lm.Level})) {{ - var s = new __{lm.Name}Struct__({GenArguments(lm)}); - logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, s, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.Format()" : "\"" + lm.Message + "\"")}); + var message = new __{lm.Name}Struct__({GenArguments(lm)}); + logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, message, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.Format()" : "\"" + lm.Message + "\"")}); }} }} "; From 056e81cdca2d501c465fe4a903f9c3b002bc046b Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 18 Nov 2020 08:29:55 -0800 Subject: [PATCH 014/121] Minor refactoring --- .../gen/LoggingGenerator.Parse.cs | 10 ++-------- .../gen/LoggingGenerator.cs | 13 +++++++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs index 3121027af4ddf..49e6af2833eb3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs @@ -98,14 +98,8 @@ public partial class LoggingGenerator /// /// Gets the set of logging classes that should be generated based on the discovered annotated interfaces. /// - private static IEnumerable GetLogClasses(GeneratorExecutionContext context, Compilation compilation) + private static IEnumerable GetLogClasses(GeneratorExecutionContext context, Compilation compilation, List interfaces) { - if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) - { - // nothing to do yet - yield break; - } - var logExtensionsAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); if (logExtensionsAttribute is null) { @@ -136,7 +130,7 @@ private static IEnumerable GetLogClasses(GeneratorExecutionContext var semanticModelMap = new Dictionary(); - foreach (var iface in receiver.InterfaceDeclarations) + foreach (var iface in interfaces) { foreach (var al in iface.AttributeLists) { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 88f5d240d137d..fce83bf96a29e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -21,8 +21,14 @@ public void Initialize(GeneratorInitializationContext context) /// public void Execute(GeneratorExecutionContext context) { + if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) + { + // nothing to do yet + return; + } + var types = new StringBuilder(); - foreach (var lc in GetLogClasses(context, context.Compilation)) + foreach (var lc in GetLogClasses(context, context.Compilation, receiver.InterfaceDeclarations)) { types.Append(GenType(lc)); } @@ -106,8 +112,7 @@ private static string GenStruct(LoggerMethod lm) if (lm.MessageHasTemplates) { format = $@" - public string Format() => $""{lm.Message}""; - public override string ToString() => Format(); + public override string ToString() => $""{lm.Message}""; "; } @@ -174,7 +179,7 @@ private static string GenExtensionLogMethod(LoggerMethod lm) if (logger.IsEnabled((LogLevel){lm.Level})) {{ var message = new __{lm.Name}Struct__({GenArguments(lm)}); - logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, message, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.Format()" : "\"" + lm.Message + "\"")}); + logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, message, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.ToString()" : "\"" + lm.Message + "\"")}); }} }} "; From b72ec2d490f7b4a8fdaf537d5a0ec6041917465a Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 18 Nov 2020 08:56:29 -0800 Subject: [PATCH 015/121] More refactoring --- .../gen/LoggingGenerator.Parse.cs | 401 ----------------- .../gen/LoggingGenerator.Parser.cs | 420 ++++++++++++++++++ .../gen/LoggingGenerator.cs | 4 +- 3 files changed, 423 insertions(+), 402 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs deleted file mode 100644 index 49e6af2833eb3..0000000000000 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parse.cs +++ /dev/null @@ -1,401 +0,0 @@ -// © Microsoft Corporation. All rights reserved. - -namespace Microsoft.Extensions.Logging.Generators -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - - public partial class LoggingGenerator - { - private const string DiagnosticCategory = "LoggingGenerator"; - -#pragma warning disable RS2008 // Enable analyzer release tracking - - private static readonly DiagnosticDescriptor ErrorInvalidMethodName = new( - id: "LG0", - title: "Logging method names cannot start with __", - messageFormat: "Logging method names cannot start with __", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorInvalidMessage = new( - id: "LG1", - title: "Missing message for logging method", - messageFormat: "Missing message for logging method {0}", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorInvalidParameterName = new( - id: "LG2", - title: "Logging method parameter names cannot start with __", - messageFormat: "Logging method parameter names cannot start with __", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorInvalidTypeName = new( - id: "LG3", - title: "Unable to automatically derive generated type name", - messageFormat: "Unable to automatically derive a generated type name based on the interface name of {0}, please specify an explicit generated type name instead", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorNestedType = new( - id: "LG4", - title: "Logging interfaces cannot be in nested types", - messageFormat: "Logging interfaces cannot be in nested types", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorMissingRequiredType = new( - id: "LG5", - title: "Could not find a required type definition", - messageFormat: "Could not find definition for type {0}", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorEventIdReuse = new( - id: "LG6", - title: "Multiple logging messages cannot use the same event id", - messageFormat: "Multiple logging messages are using event id {0}", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorInvalidMethodReturnType = new( - id: "LG7", - title: "Logging methods must return void", - messageFormat: "Logging methods must return void", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorInterfaceGeneric = new( - id: "LG8", - title: "Logging interfaces cannot be generic", - messageFormat: "Logging interfaces cannot be generic", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorMethodGeneric = new( - id: "LG9", - title: "Logging methods cannot be generic", - messageFormat: "Logging methods cannot be generic", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - /// - /// Gets the set of logging classes that should be generated based on the discovered annotated interfaces. - /// - private static IEnumerable GetLogClasses(GeneratorExecutionContext context, Compilation compilation, List interfaces) - { - var logExtensionsAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); - if (logExtensionsAttribute is null) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerExtensionsAttribute")); - yield break; - } - - var loggerMessageAttribute = compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); - if (loggerMessageAttribute is null) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute")); - yield break; - } - - var exSymbol = compilation.GetTypeByMetadataName("System.Exception"); - if (exSymbol == null) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingRequiredType, null, "System.Exception")); - yield break; - } - - var voidSymbol = compilation.GetTypeByMetadataName("System.Void"); - if (voidSymbol == null) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorMissingRequiredType, null, "System.Void")); - yield break; - } - - var semanticModelMap = new Dictionary(); - - foreach (var iface in interfaces) - { - foreach (var al in iface.AttributeLists) - { - foreach (var a in al.Attributes) - { - // does this interface have the [LoggerExtensions] atribute? - var semanticModel = GetSemanticModel(compilation, semanticModelMap, a.SyntaxTree); - var aSymbol = semanticModel.GetSymbolInfo(a, context.CancellationToken); - if (aSymbol.Symbol is IMethodSymbol methodSymbol && logExtensionsAttribute.Equals(methodSymbol.ContainingType, SymbolEqualityComparer.Default)) - { - // determine the namespace the interface is declared in, if any - NamespaceDeclarationSyntax? ns = null; - if (iface.Parent != null) - { - ns = iface.Parent as NamespaceDeclarationSyntax; - if (ns == null && iface.Parent is not CompilationUnitSyntax) - { - // since this generator doesn't know how to generate a nested type... - context.ReportDiagnostic(Diagnostic.Create(ErrorNestedType, iface.Identifier.GetLocation())); - } - } - - // determine the name of the generated type - - string? name = null; - if (a.ArgumentList?.Arguments.Count > 0) - { - var arg = a.ArgumentList!.Arguments[0]; - name = semanticModel.GetConstantValue(arg.Expression).ToString(); - } - - // if no name was specified, try to derive it from the interface name - var ifaceName = iface.Identifier.ToString(); - if (name == null) - { - if (ifaceName[0] == 'I') - { - // strip the leading I - name = ifaceName.Substring(1); - } - else - { - // fail - name = string.Empty; - } - } - - var lc = new LoggerClass - { - Namespace = ns?.Name.ToString(), - Name = name, - OriginalInterfaceName = iface.Identifier.ToString(), - AccessModifiers = iface.Modifiers.ToString(), - Documentation = GetDocs(iface), - }; - - if (string.IsNullOrWhiteSpace(lc.Name)) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidTypeName, a.GetLocation(), ifaceName)); - } - - if (iface.Arity > 0) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorInterfaceGeneric, iface.GetLocation())); - } - - var ids = new HashSet(); - foreach (var method in iface.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) - { - foreach (var mal in method.AttributeLists) - { - foreach (var ma in mal.Attributes) - { - semanticModel = GetSemanticModel(compilation, semanticModelMap, ma.SyntaxTree); - var maSymbol = semanticModel.GetSymbolInfo(ma, context.CancellationToken); - if (maSymbol.Symbol is IMethodSymbol ms && loggerMessageAttribute.Equals(ms.ContainingType, SymbolEqualityComparer.Default)) - { - var arg = ma.ArgumentList!.Arguments[0]; - var eventId = semanticModel.GetConstantValue(arg.Expression).ToString(); - - arg = ma.ArgumentList!.Arguments[1]; - var level = semanticModel.GetConstantValue(arg.Expression).ToString(); - - arg = ma.ArgumentList!.Arguments[2]; - var message = semanticModel.GetConstantValue(arg.Expression).ToString(); - - string? eventName = null; - - if (ma.ArgumentList?.Arguments is { Count: > 3 } args) - { - arg = args[3]; - eventName = semanticModel.GetConstantValue(arg.Expression).ToString(); - } - - var lm = new LoggerMethod - { - Name = method.Identifier.ToString(), - EventId = eventId, - Level = level, - Message = message, - EventName = eventName, - MessageHasTemplates = HasTemplates(message), - Documentation = GetDocs(method), - }; - lc.Methods.Add(lm); - - if (lm.Name.StartsWith("__", StringComparison.Ordinal)) - { - // can't have logging method names that start with __ since that can lead to conflicting symbol names - // because the generated symbols start with __ - context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodName, method.Identifier.GetLocation())); - } - - if (!GetSemanticModel(compilation, semanticModelMap, method.ReturnType.SyntaxTree).GetTypeInfo(method.ReturnType!).Type!.Equals(voidSymbol, SymbolEqualityComparer.Default)) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMethodReturnType, method.ReturnType.GetLocation())); - } - - if (method.Arity > 0) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorMethodGeneric, method.GetLocation())); - } - - // ensure there are no duplicate ids. - if (ids.Contains(lm.EventId)) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorEventIdReuse, ma.ArgumentList!.Arguments[0].GetLocation(), lm.EventId)); - } - else - { - ids.Add(lm.EventId); - } - - if (string.IsNullOrWhiteSpace(lm.Message)) - { - context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidMessage, ma.GetLocation(), method.Identifier.ToString())); - } - - foreach (var p in method.ParameterList.Parameters) - { - var pSymbol = GetSemanticModel(compilation, semanticModelMap, p.SyntaxTree).GetTypeInfo(p.Type!).Type!; - - var lp = new LoggerParameter - { - Name = p.Identifier.ToString(), - Type = p.Type!.ToString(), - IsExceptionType = IsBaseOrIdentity(compilation, pSymbol, exSymbol), - }; - lm.Parameters.Add(lp); - - if (lp.Name.StartsWith("__", StringComparison.Ordinal)) - { - // can't have logging method parameter names that start with __ since that can lead to conflicting symbol names - // because all generated symbols start with __ - context.ReportDiagnostic(Diagnostic.Create(ErrorInvalidParameterName, p.Identifier.GetLocation())); - } - } - } - } - } - } - - yield return lc; - } - } - } - } - } - - static string GetDocs(SyntaxNode _) - { -#if false -This is not ready yet - foreach (var trivia in node.GetLeadingTrivia().Where(x => x.HasStructure)) - { - if (trivia.GetStructure() is DocumentationCommentTriviaSyntax sTrivia) - { - return sTrivia.ToString(); - } - } -#endif - return string.Empty; - } - - // Workaround for https://github.com/dotnet/roslyn/pull/49330 - static SemanticModel GetSemanticModel(Compilation compilation, Dictionary semanticModelMap, SyntaxTree syntaxTree) - { - if (!semanticModelMap.TryGetValue(syntaxTree, out var semanticModel)) - { - semanticModel = compilation.GetSemanticModel(syntaxTree); - semanticModelMap[syntaxTree] = semanticModel; - } - - return semanticModel; - } - - static bool IsBaseOrIdentity(Compilation compilation, ITypeSymbol source, ITypeSymbol dest) - { - var conversion = compilation.ClassifyConversion(source, dest); - return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit); - } - - private static bool HasTemplates(string message) - { - for (int i = 0; i < message.Length; i++) - { - var ch = message[i]; - if (ch == '{') - { - if (i < message.Length - 1 && message[i + 1] != '{') - { - // look for a non-escaped } - i++; - for (; i < message.Length; i++) - { - ch = message[i]; - if (ch == '}') - { - if (i == message.Length - 1 || message[i + 1] != '}') - { - return true; - } - } - } - - return false; - } - } - } - - return false; - } - -#pragma warning disable SA1401 // Fields should be private - - // An logging class holding a bunch of log methods - private class LoggerClass - { - public string? Namespace; - public string Name = string.Empty; - public string OriginalInterfaceName = string.Empty; - public string AccessModifiers = string.Empty; - public List Methods = new(); - public string Documentation = string.Empty; - } - - // A log method in a logging class - private class LoggerMethod - { - public string Name = string.Empty; - public string Message = string.Empty; - public string Level = string.Empty; - public string EventId = string.Empty; - public string? EventName = null!; - public List Parameters = new(); - public bool MessageHasTemplates; - public string Documentation = string.Empty; - } - - // A single parameter to a log method - private class LoggerParameter - { - public string Name = string.Empty; - public string Type = string.Empty; - public bool IsExceptionType; - } - } -} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs new file mode 100644 index 0000000000000..b623db6b3ac2d --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -0,0 +1,420 @@ +// © Microsoft Corporation. All rights reserved. + +namespace Microsoft.Extensions.Logging.Generators +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + public partial class LoggingGenerator + { + private class Parser + { + private const string DiagnosticCategory = "LoggingGenerator"; + +#pragma warning disable RS2008 // Enable analyzer release tracking + + private static readonly DiagnosticDescriptor ErrorInvalidMethodName = new( + id: "LG0", + title: "Logging method names cannot start with __", + messageFormat: "Logging method names cannot start with __", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorInvalidMessage = new( + id: "LG1", + title: "Missing message for logging method", + messageFormat: "Missing message for logging method {0}", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorInvalidParameterName = new( + id: "LG2", + title: "Logging method parameter names cannot start with __", + messageFormat: "Logging method parameter names cannot start with __", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorInvalidTypeName = new( + id: "LG3", + title: "Unable to automatically derive generated type name", + messageFormat: "Unable to automatically derive a generated type name based on the interface name of {0}, please specify an explicit generated type name instead", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorNestedType = new( + id: "LG4", + title: "Logging interfaces cannot be in nested types", + messageFormat: "Logging interfaces cannot be in nested types", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorMissingRequiredType = new( + id: "LG5", + title: "Could not find a required type definition", + messageFormat: "Could not find definition for type {0}", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorEventIdReuse = new( + id: "LG6", + title: "Multiple logging messages cannot use the same event id", + messageFormat: "Multiple logging messages are using event id {0}", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorInvalidMethodReturnType = new( + id: "LG7", + title: "Logging methods must return void", + messageFormat: "Logging methods must return void", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorInterfaceGeneric = new( + id: "LG8", + title: "Logging interfaces cannot be generic", + messageFormat: "Logging interfaces cannot be generic", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorMethodGeneric = new( + id: "LG9", + title: "Logging methods cannot be generic", + messageFormat: "Logging methods cannot be generic", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private readonly Compilation _compilation; + private readonly GeneratorExecutionContext _context; + private readonly Dictionary _semanticModels = new(); + + public Parser(GeneratorExecutionContext context) + { + _context = context; + _compilation = context.Compilation; + } + + /// + /// Gets the set of logging classes that should be generated based on the discovered annotated interfaces. + /// + public IEnumerable GetLogClasses(List interfaces) + { + var logExtensionsAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); + if (logExtensionsAttribute is null) + { + Diag(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); + yield break; + } + + var loggerMessageAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); + if (loggerMessageAttribute is null) + { + Diag(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute"); + yield break; + } + + var exSymbol = _compilation.GetTypeByMetadataName("System.Exception"); + if (exSymbol == null) + { + Diag(ErrorMissingRequiredType, null, "System.Exception"); + yield break; + } + + var voidSymbol = _compilation.GetTypeByMetadataName("System.Void"); + if (voidSymbol == null) + { + Diag(ErrorMissingRequiredType, null, "System.Void"); + yield break; + } + + foreach (var iface in interfaces) + { + foreach (var al in iface.AttributeLists) + { + foreach (var a in al.Attributes) + { + // does this interface have the [LoggerExtensions] atribute? + var semanticModel = GetSemanticModel(a.SyntaxTree); + var aSymbol = semanticModel.GetSymbolInfo(a, _context.CancellationToken); + if (aSymbol.Symbol is IMethodSymbol methodSymbol && logExtensionsAttribute.Equals(methodSymbol.ContainingType, SymbolEqualityComparer.Default)) + { + // determine the namespace the interface is declared in, if any + NamespaceDeclarationSyntax? ns = null; + if (iface.Parent != null) + { + ns = iface.Parent as NamespaceDeclarationSyntax; + if (ns == null && iface.Parent is not CompilationUnitSyntax) + { + // since this generator doesn't know how to generate a nested type... + Diag(ErrorNestedType, iface.Identifier.GetLocation()); + } + } + + // determine the name of the generated type + + string? name = null; + if (a.ArgumentList?.Arguments.Count > 0) + { + var arg = a.ArgumentList!.Arguments[0]; + name = semanticModel.GetConstantValue(arg.Expression).ToString(); + } + + // if no name was specified, try to derive it from the interface name + var ifaceName = iface.Identifier.ToString(); + if (name == null) + { + if (ifaceName[0] == 'I') + { + // strip the leading I + name = ifaceName.Substring(1); + } + else + { + // fail + name = string.Empty; + } + } + + var lc = new LoggerClass + { + Namespace = ns?.Name.ToString(), + Name = name, + OriginalInterfaceName = iface.Identifier.ToString(), + AccessModifiers = iface.Modifiers.ToString(), + Documentation = GetDocs(iface), + }; + + if (string.IsNullOrWhiteSpace(lc.Name)) + { + Diag(ErrorInvalidTypeName, a.GetLocation(), ifaceName); + } + + if (iface.Arity > 0) + { + Diag(ErrorInterfaceGeneric, iface.GetLocation()); + } + + var ids = new HashSet(); + foreach (var method in iface.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) + { + foreach (var mal in method.AttributeLists) + { + foreach (var ma in mal.Attributes) + { + semanticModel = GetSemanticModel(ma.SyntaxTree); + var maSymbol = semanticModel.GetSymbolInfo(ma, _context.CancellationToken); + if (maSymbol.Symbol is IMethodSymbol ms && loggerMessageAttribute.Equals(ms.ContainingType, SymbolEqualityComparer.Default)) + { + var arg = ma.ArgumentList!.Arguments[0]; + var eventId = semanticModel.GetConstantValue(arg.Expression).ToString(); + + arg = ma.ArgumentList!.Arguments[1]; + var level = semanticModel.GetConstantValue(arg.Expression).ToString(); + + arg = ma.ArgumentList!.Arguments[2]; + var message = semanticModel.GetConstantValue(arg.Expression).ToString(); + + string? eventName = null; + + if (ma.ArgumentList?.Arguments is { Count: > 3 } args) + { + arg = args[3]; + eventName = semanticModel.GetConstantValue(arg.Expression).ToString(); + } + + var lm = new LoggerMethod + { + Name = method.Identifier.ToString(), + EventId = eventId, + Level = level, + Message = message, + EventName = eventName, + MessageHasTemplates = HasTemplates(message), + Documentation = GetDocs(method), + }; + lc.Methods.Add(lm); + + if (lm.Name.StartsWith("__", StringComparison.Ordinal)) + { + // can't have logging method names that start with __ since that can lead to conflicting symbol names + // because the generated symbols start with __ + Diag(ErrorInvalidMethodName, method.Identifier.GetLocation()); + } + + if (!GetSemanticModel(method.ReturnType.SyntaxTree).GetTypeInfo(method.ReturnType!).Type!.Equals(voidSymbol, SymbolEqualityComparer.Default)) + { + Diag(ErrorInvalidMethodReturnType, method.ReturnType.GetLocation()); + } + + if (method.Arity > 0) + { + Diag(ErrorMethodGeneric, method.GetLocation()); + } + + // ensure there are no duplicate ids. + if (ids.Contains(lm.EventId)) + { + Diag(ErrorEventIdReuse, ma.ArgumentList!.Arguments[0].GetLocation(), lm.EventId); + } + else + { + ids.Add(lm.EventId); + } + + if (string.IsNullOrWhiteSpace(lm.Message)) + { + Diag(ErrorInvalidMessage, ma.GetLocation(), method.Identifier.ToString()); + } + + foreach (var p in method.ParameterList.Parameters) + { + var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; + + var lp = new LoggerParameter + { + Name = p.Identifier.ToString(), + Type = p.Type!.ToString(), + IsExceptionType = IsBaseOrIdentity(pSymbol, exSymbol), + }; + lm.Parameters.Add(lp); + + if (lp.Name.StartsWith("__", StringComparison.Ordinal)) + { + // can't have logging method parameter names that start with __ since that can lead to conflicting symbol names + // because all generated symbols start with __ + Diag(ErrorInvalidParameterName, p.Identifier.GetLocation()); + } + } + } + } + } + } + + yield return lc; + } + } + } + } + } + + private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs) + { + _context.ReportDiagnostic(Diagnostic.Create(desc, location, messageArgs)); + } + + private static string GetDocs(SyntaxNode _) + { +#if false + This is not ready yet + foreach (var trivia in node.GetLeadingTrivia().Where(x => x.HasStructure)) + { + if (trivia.GetStructure() is DocumentationCommentTriviaSyntax sTrivia) + { + return sTrivia.ToString(); + } + } +#endif + return string.Empty; + } + + // Workaround for https://github.com/dotnet/roslyn/pull/49330 + private SemanticModel GetSemanticModel(SyntaxTree syntaxTree) + { + if (!_semanticModels.TryGetValue(syntaxTree, out var semanticModel)) + { + semanticModel = _compilation.GetSemanticModel(syntaxTree); + _semanticModels[syntaxTree] = semanticModel; + } + + return semanticModel; + } + + private bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest) + { + var conversion = _compilation.ClassifyConversion(source, dest); + return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit); + } + + /// + /// Does the string contain templates? + /// + private static bool HasTemplates(string message) + { + for (int i = 0; i < message.Length; i++) + { + var ch = message[i]; + if (ch == '{') + { + if (i < message.Length - 1 && message[i + 1] != '{') + { + // look for a non-escaped } + i++; + for (; i < message.Length; i++) + { + ch = message[i]; + if (ch == '}') + { + if (i == message.Length - 1 || message[i + 1] != '}') + { + return true; + } + } + } + + return false; + } + } + } + + return false; + } + } + +#pragma warning disable SA1401 // Fields should be private + + // An logging class holding a bunch of log methods + private class LoggerClass + { + public string? Namespace; + public string Name = string.Empty; + public string OriginalInterfaceName = string.Empty; + public string AccessModifiers = string.Empty; + public List Methods = new(); + public string Documentation = string.Empty; + } + + // A log method in a logging class + private class LoggerMethod + { + public string Name = string.Empty; + public string Message = string.Empty; + public string Level = string.Empty; + public string EventId = string.Empty; + public string? EventName = null!; + public List Parameters = new(); + public bool MessageHasTemplates; + public string Documentation = string.Empty; + } + + // A single parameter to a log method + private class LoggerParameter + { + public string Name = string.Empty; + public string Type = string.Empty; + public bool IsExceptionType; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index fce83bf96a29e..b2d10b382dfed 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -27,8 +27,10 @@ public void Execute(GeneratorExecutionContext context) return; } + var p = new Parser(context); + var types = new StringBuilder(); - foreach (var lc in GetLogClasses(context, context.Compilation, receiver.InterfaceDeclarations)) + foreach (var lc in p.GetLogClasses(receiver.InterfaceDeclarations)) { types.Append(GenType(lc)); } From 4ab2acc2e376378711cabf7bd0a5adccf0748bd2 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Wed, 18 Nov 2020 20:14:21 -0400 Subject: [PATCH 016/121] Support partial methods (#4) --- .../gen/LoggingGenerator.Parser.cs | 51 ++++++++++++------- .../gen/LoggingGenerator.cs | 30 ++++++++--- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index b623db6b3ac2d..aed2699dd7595 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -110,7 +110,7 @@ public Parser(GeneratorExecutionContext context) /// /// Gets the set of logging classes that should be generated based on the discovered annotated interfaces. /// - public IEnumerable GetLogClasses(List interfaces) + public IEnumerable GetLogClasses(List types) { var logExtensionsAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); if (logExtensionsAttribute is null) @@ -140,9 +140,9 @@ public IEnumerable GetLogClasses(List i yield break; } - foreach (var iface in interfaces) + foreach (var typeDef in types) { - foreach (var al in iface.AttributeLists) + foreach (var al in typeDef.AttributeLists) { foreach (var a in al.Attributes) { @@ -153,13 +153,13 @@ public IEnumerable GetLogClasses(List i { // determine the namespace the interface is declared in, if any NamespaceDeclarationSyntax? ns = null; - if (iface.Parent != null) + if (typeDef.Parent != null) { - ns = iface.Parent as NamespaceDeclarationSyntax; - if (ns == null && iface.Parent is not CompilationUnitSyntax) + ns = typeDef.Parent as NamespaceDeclarationSyntax; + if (ns == null && typeDef.Parent is not CompilationUnitSyntax) { // since this generator doesn't know how to generate a nested type... - Diag(ErrorNestedType, iface.Identifier.GetLocation()); + Diag(ErrorNestedType, typeDef.Identifier.GetLocation()); } } @@ -173,18 +173,23 @@ public IEnumerable GetLogClasses(List i } // if no name was specified, try to derive it from the interface name - var ifaceName = iface.Identifier.ToString(); + var ifaceName = typeDef.Identifier.ToString(); if (name == null) { - if (ifaceName[0] == 'I') + if (typeDef is InterfaceDeclarationSyntax iface) { - // strip the leading I - name = ifaceName.Substring(1); + if (ifaceName[0] == 'I' && ifaceName.Length > 1) + { + name = ifaceName.Substring(1); + } + else + { + name = ifaceName + "Extensions"; + } } else { - // fail - name = string.Empty; + name = typeDef.Identifier.ToString(); } } @@ -192,9 +197,10 @@ public IEnumerable GetLogClasses(List i { Namespace = ns?.Name.ToString(), Name = name, - OriginalInterfaceName = iface.Identifier.ToString(), - AccessModifiers = iface.Modifiers.ToString(), - Documentation = GetDocs(iface), + OriginalInterfaceName = typeDef.Identifier.ToString(), + AccessModifiers = typeDef.Modifiers.ToString(), + Documentation = GetDocs(typeDef), + IsInterface = typeDef is InterfaceDeclarationSyntax }; if (string.IsNullOrWhiteSpace(lc.Name)) @@ -202,13 +208,13 @@ public IEnumerable GetLogClasses(List i Diag(ErrorInvalidTypeName, a.GetLocation(), ifaceName); } - if (iface.Arity > 0) + if (typeDef.Arity > 0) { - Diag(ErrorInterfaceGeneric, iface.GetLocation()); + Diag(ErrorInterfaceGeneric, typeDef.GetLocation()); } var ids = new HashSet(); - foreach (var method in iface.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) + foreach (var method in typeDef.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) { foreach (var mal in method.AttributeLists) { @@ -289,6 +295,12 @@ public IEnumerable GetLogClasses(List i Type = p.Type!.ToString(), IsExceptionType = IsBaseOrIdentity(pSymbol, exSymbol), }; + + if (lp.Type.EndsWith("ILogger", StringComparison.Ordinal)) + { + continue; + } + lm.Parameters.Add(lp); if (lp.Name.StartsWith("__", StringComparison.Ordinal)) @@ -394,6 +406,7 @@ private class LoggerClass public string AccessModifiers = string.Empty; public List Methods = new(); public string Documentation = string.Empty; + public bool IsInterface { get; set; } } // A log method in a logging class diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index b2d10b382dfed..644bb84dfa71c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -6,6 +6,7 @@ namespace Microsoft.Extensions.Logging.Generators using System.Collections.Generic; using System.Text; using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; @@ -30,7 +31,7 @@ public void Execute(GeneratorExecutionContext context) var p = new Parser(context); var types = new StringBuilder(); - foreach (var lc in p.GetLogClasses(receiver.InterfaceDeclarations)) + foreach (var lc in p.GetLogClasses(receiver.TypeDeclarations)) { types.Append(GenType(lc)); } @@ -53,7 +54,7 @@ private static string GenType(LoggerClass lc) { methods.Append(GenStruct(lm)); methods.Append(GenEventId(lm)); - methods.Append(GenExtensionLogMethod(lm)); + methods.Append(GenExtensionLogMethod(lc, lm)); } var namespaceStart = string.Empty; @@ -68,10 +69,9 @@ private static string GenType(LoggerClass lc) return $@" {namespaceStart} {lc.Documentation} - {lc.AccessModifiers} static class {lc.Name} + {lc.AccessModifiers} {(lc.IsInterface ? "static" : "")} class {lc.Name} {{ {methods} - public static {lc.OriginalInterfaceName} Wrap(this ILogger logger) => new __Wrapper__(logger); {GenWrapper(lc)} }} {namespaceEnd} @@ -80,6 +80,10 @@ private static string GenType(LoggerClass lc) private static string GenWrapper(LoggerClass lc) { + if (!lc.IsInterface) + { + return ""; + } var methods = new StringBuilder(); foreach (var lm in lc.Methods) { @@ -87,6 +91,8 @@ private static string GenWrapper(LoggerClass lc) } return $@" + public static {lc.OriginalInterfaceName} Wrap(this ILogger logger) => new __Wrapper__(logger); + private sealed class __Wrapper__ : {lc.OriginalInterfaceName} {{ private readonly ILogger __logger; @@ -162,7 +168,7 @@ private static string GenInstanceLogMethod(LoggerMethod lm) "; } - private static string GenExtensionLogMethod(LoggerMethod lm) + private static string GenExtensionLogMethod(LoggerClass lc, LoggerMethod lm) { string exceptionArg = "null"; foreach (var p in lm.Parameters) @@ -174,9 +180,11 @@ private static string GenExtensionLogMethod(LoggerMethod lm) } } + var loggerArg = lc.IsInterface ? "this ILogger logger" : "ILogger logger"; + return $@" {lm.Documentation} - public static void {lm.Name}(this ILogger logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + public static {(lc.IsInterface ? "" : "partial")} void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (logger.IsEnabled((LogLevel){lm.Level})) {{ @@ -299,13 +307,19 @@ private static string GenCases(LoggerMethod lm) private sealed class SyntaxReceiver : ISyntaxReceiver { - public List InterfaceDeclarations { get; } = new(); + public List TypeDeclarations { get; } = new(); public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { if (syntaxNode is InterfaceDeclarationSyntax interfaceSyntax && interfaceSyntax.AttributeLists.Count > 0) { - InterfaceDeclarations.Add(interfaceSyntax); + TypeDeclarations.Add(interfaceSyntax); + } + + // Any partial class + if (syntaxNode is ClassDeclarationSyntax { Modifiers: { Count: > 0 } modifiers } classSyntax && modifiers.Any(SyntaxKind.StaticKeyword) && modifiers.Any(SyntaxKind.PartialKeyword)) + { + TypeDeclarations.Add(classSyntax); } } } From 07182930c3898c2d8716973c735c3f046b9a6ef4 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 18 Nov 2020 19:05:29 -0800 Subject: [PATCH 017/121] Optimize some code gen. - GetEnumerator is now implemented by calling the indexer to avoid some redundant code. - Logging methods without arguments now share a common log state struct, which eliminates redundant code to JIT. --- .../gen/LoggingGenerator.cs | 43 +++++++------------ 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 644bb84dfa71c..148387e24ffd8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -52,7 +52,10 @@ private static string GenType(LoggerClass lc) var methods = new StringBuilder(); foreach (var lm in lc.Methods) { - methods.Append(GenStruct(lm)); + if (lm.Parameters.Count > 0) + { + methods.Append(GenStruct(lm)); + } methods.Append(GenEventId(lm)); methods.Append(GenExtensionLogMethod(lc, lm)); } @@ -105,16 +108,12 @@ private sealed class __Wrapper__ : {lc.OriginalInterfaceName} private static string GenStruct(LoggerMethod lm) { - var constructor = string.Empty; - if (lm.Parameters.Count > 0) - { - constructor = $@" + var constructor = $@" public __{lm.Name}Struct__({GenParameters(lm)}) {{ {GenFieldAssignments(lm)} }} "; - } var format = string.Empty; if (lm.MessageHasTemplates) @@ -181,6 +180,13 @@ private static string GenExtensionLogMethod(LoggerClass lc, LoggerMethod lm) } var loggerArg = lc.IsInterface ? "this ILogger logger" : "ILogger logger"; + + var ctorCall = $"__{lm.Name}Struct__({ GenArguments(lm)})"; + if (lm.Parameters.Count == 0) + { + // when no parameters, use the common struct + ctorCall = "Microsoft.Extensions.Logging.LogStateHolder()"; + } return $@" {lm.Documentation} @@ -188,7 +194,7 @@ private static string GenExtensionLogMethod(LoggerClass lc, LoggerMethod lm) {{ if (logger.IsEnabled((LogLevel){lm.Level})) {{ - var message = new __{lm.Name}Struct__({GenArguments(lm)}); + var message = new {ctorCall}; logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, message, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.ToString()" : "\"" + lm.Message + "\"")}); }} }} @@ -241,11 +247,6 @@ private static string GenArguments(LoggerMethod lm) private static string GenFields(LoggerMethod lm) { - if (lm.Parameters.Count == 0) - { - return string.Empty; - } - var sb = new StringBuilder(); foreach (var p in lm.Parameters) { @@ -257,11 +258,6 @@ private static string GenFields(LoggerMethod lm) private static string GenFieldAssignments(LoggerMethod lm) { - if (lm.Parameters.Count == 0) - { - return string.Empty; - } - var sb = new StringBuilder(); foreach (var p in lm.Parameters) { @@ -273,15 +269,11 @@ private static string GenFieldAssignments(LoggerMethod lm) private static string GenEnumerator(LoggerMethod lm) { - if (lm.Parameters.Count == 0) - { - return " yield break;\n"; - } - var sb = new StringBuilder(); + int index = 0; foreach (var p in lm.Parameters) { - sb.Append($" yield return new KeyValuePair(nameof({p.Name}), {p.Name});\n"); + sb.Append($" yield return this[{index}];\n"); } return sb.ToString(); @@ -289,11 +281,6 @@ private static string GenEnumerator(LoggerMethod lm) private static string GenCases(LoggerMethod lm) { - if (lm.Parameters.Count == 0) - { - return string.Empty; - } - var sb = new StringBuilder(); var index = 0; foreach (var p in lm.Parameters) From d065c905ce25c771a8046f95f0c96815aece543a Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 18 Nov 2020 20:10:43 -0800 Subject: [PATCH 018/121] Simplify the model. You can now only annotate partial methods --- .../gen/LoggingGenerator.Parser.cs | 310 ++++++------------ .../gen/LoggingGenerator.cs | 62 +--- .../LoggingTests.cs | 131 ++------ ...Extensions.Logging.Generators.Tests.csproj | 1 + 4 files changed, 139 insertions(+), 365 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index aed2699dd7595..07d31be976b38 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -41,24 +41,16 @@ private class Parser DiagnosticSeverity.Error, isEnabledByDefault: true); - private static readonly DiagnosticDescriptor ErrorInvalidTypeName = new( - id: "LG3", - title: "Unable to automatically derive generated type name", - messageFormat: "Unable to automatically derive a generated type name based on the interface name of {0}, please specify an explicit generated type name instead", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - private static readonly DiagnosticDescriptor ErrorNestedType = new( - id: "LG4", - title: "Logging interfaces cannot be in nested types", - messageFormat: "Logging interfaces cannot be in nested types", + id: "LG3", + title: "Logging class cannot be in nested types", + messageFormat: "Logging class cannot be in nested types", category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorMissingRequiredType = new( - id: "LG5", + id: "LG4", title: "Could not find a required type definition", messageFormat: "Could not find definition for type {0}", category: DiagnosticCategory, @@ -66,7 +58,7 @@ private class Parser isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorEventIdReuse = new( - id: "LG6", + id: "LG5", title: "Multiple logging messages cannot use the same event id", messageFormat: "Multiple logging messages are using event id {0}", category: DiagnosticCategory, @@ -74,23 +66,15 @@ private class Parser isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidMethodReturnType = new( - id: "LG7", + id: "LG6", title: "Logging methods must return void", messageFormat: "Logging methods must return void", category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); - private static readonly DiagnosticDescriptor ErrorInterfaceGeneric = new( - id: "LG8", - title: "Logging interfaces cannot be generic", - messageFormat: "Logging interfaces cannot be generic", - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - private static readonly DiagnosticDescriptor ErrorMethodGeneric = new( - id: "LG9", + id: "LG7", title: "Logging methods cannot be generic", messageFormat: "Logging methods cannot be generic", category: DiagnosticCategory, @@ -108,17 +92,10 @@ public Parser(GeneratorExecutionContext context) } /// - /// Gets the set of logging classes that should be generated based on the discovered annotated interfaces. + /// Gets the set of logging classes contains methods to output. /// - public IEnumerable GetLogClasses(List types) + public IEnumerable GetLogClasses(List classes) { - var logExtensionsAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); - if (logExtensionsAttribute is null) - { - Diag(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerExtensionsAttribute"); - yield break; - } - var loggerMessageAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); if (loggerMessageAttribute is null) { @@ -140,185 +117,128 @@ public IEnumerable GetLogClasses(List types) yield break; } - foreach (var typeDef in types) + foreach (var classDef in classes) { - foreach (var al in typeDef.AttributeLists) + // determine the namespace the class is declared in, if any + NamespaceDeclarationSyntax? ns = null; + if (classDef.Parent != null) + { + ns = classDef.Parent as NamespaceDeclarationSyntax; + if (ns == null && classDef.Parent is not CompilationUnitSyntax) + { + // since this generator doesn't know how to generate a nested type... + Diag(ErrorNestedType, classDef.Identifier.GetLocation()); + } + } + + var lc = new LoggerClass + { + Namespace = ns?.Name.ToString(), + Name = classDef.Identifier.ToString(), + }; + + var ids = new HashSet(); + foreach (var method in classDef.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) { - foreach (var a in al.Attributes) + foreach (var mal in method.AttributeLists) { - // does this interface have the [LoggerExtensions] atribute? - var semanticModel = GetSemanticModel(a.SyntaxTree); - var aSymbol = semanticModel.GetSymbolInfo(a, _context.CancellationToken); - if (aSymbol.Symbol is IMethodSymbol methodSymbol && logExtensionsAttribute.Equals(methodSymbol.ContainingType, SymbolEqualityComparer.Default)) + foreach (var ma in mal.Attributes) { - // determine the namespace the interface is declared in, if any - NamespaceDeclarationSyntax? ns = null; - if (typeDef.Parent != null) + var semanticModel = GetSemanticModel(ma.SyntaxTree); + var maSymbol = semanticModel.GetSymbolInfo(ma, _context.CancellationToken); + if (maSymbol.Symbol is IMethodSymbol ms && loggerMessageAttribute.Equals(ms.ContainingType, SymbolEqualityComparer.Default)) { - ns = typeDef.Parent as NamespaceDeclarationSyntax; - if (ns == null && typeDef.Parent is not CompilationUnitSyntax) + var arg = ma.ArgumentList!.Arguments[0]; + var eventId = semanticModel.GetConstantValue(arg.Expression).ToString(); + + arg = ma.ArgumentList!.Arguments[1]; + var level = semanticModel.GetConstantValue(arg.Expression).ToString(); + + arg = ma.ArgumentList!.Arguments[2]; + var message = semanticModel.GetConstantValue(arg.Expression).ToString(); + + string? eventName = null; + + if (ma.ArgumentList?.Arguments is { Count: > 3 } args) { - // since this generator doesn't know how to generate a nested type... - Diag(ErrorNestedType, typeDef.Identifier.GetLocation()); + arg = args[3]; + eventName = semanticModel.GetConstantValue(arg.Expression).ToString(); + } + + var lm = new LoggerMethod + { + Name = method.Identifier.ToString(), + EventId = eventId, + Level = level, + Message = message, + EventName = eventName, + MessageHasTemplates = HasTemplates(message), + }; + lc.Methods.Add(lm); + + if (lm.Name.StartsWith("__", StringComparison.Ordinal)) + { + // can't have logging method names that start with __ since that can lead to conflicting symbol names + // because the generated symbols start with __ + Diag(ErrorInvalidMethodName, method.Identifier.GetLocation()); } - } - // determine the name of the generated type + if (!GetSemanticModel(method.ReturnType.SyntaxTree).GetTypeInfo(method.ReturnType!).Type!.Equals(voidSymbol, SymbolEqualityComparer.Default)) + { + Diag(ErrorInvalidMethodReturnType, method.ReturnType.GetLocation()); + } - string? name = null; - if (a.ArgumentList?.Arguments.Count > 0) - { - var arg = a.ArgumentList!.Arguments[0]; - name = semanticModel.GetConstantValue(arg.Expression).ToString(); - } + if (method.Arity > 0) + { + Diag(ErrorMethodGeneric, method.GetLocation()); + } - // if no name was specified, try to derive it from the interface name - var ifaceName = typeDef.Identifier.ToString(); - if (name == null) - { - if (typeDef is InterfaceDeclarationSyntax iface) + // ensure there are no duplicate ids. + if (ids.Contains(lm.EventId)) { - if (ifaceName[0] == 'I' && ifaceName.Length > 1) - { - name = ifaceName.Substring(1); - } - else - { - name = ifaceName + "Extensions"; - } + Diag(ErrorEventIdReuse, ma.ArgumentList!.Arguments[0].GetLocation(), lm.EventId); } else { - name = typeDef.Identifier.ToString(); + ids.Add(lm.EventId); } - } - var lc = new LoggerClass - { - Namespace = ns?.Name.ToString(), - Name = name, - OriginalInterfaceName = typeDef.Identifier.ToString(), - AccessModifiers = typeDef.Modifiers.ToString(), - Documentation = GetDocs(typeDef), - IsInterface = typeDef is InterfaceDeclarationSyntax - }; - - if (string.IsNullOrWhiteSpace(lc.Name)) - { - Diag(ErrorInvalidTypeName, a.GetLocation(), ifaceName); - } - - if (typeDef.Arity > 0) - { - Diag(ErrorInterfaceGeneric, typeDef.GetLocation()); - } + if (string.IsNullOrWhiteSpace(lm.Message)) + { + Diag(ErrorInvalidMessage, ma.GetLocation(), method.Identifier.ToString()); + } - var ids = new HashSet(); - foreach (var method in typeDef.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) - { - foreach (var mal in method.AttributeLists) + foreach (var p in method.ParameterList.Parameters) { - foreach (var ma in mal.Attributes) + var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; + + var lp = new LoggerParameter { - semanticModel = GetSemanticModel(ma.SyntaxTree); - var maSymbol = semanticModel.GetSymbolInfo(ma, _context.CancellationToken); - if (maSymbol.Symbol is IMethodSymbol ms && loggerMessageAttribute.Equals(ms.ContainingType, SymbolEqualityComparer.Default)) - { - var arg = ma.ArgumentList!.Arguments[0]; - var eventId = semanticModel.GetConstantValue(arg.Expression).ToString(); - - arg = ma.ArgumentList!.Arguments[1]; - var level = semanticModel.GetConstantValue(arg.Expression).ToString(); - - arg = ma.ArgumentList!.Arguments[2]; - var message = semanticModel.GetConstantValue(arg.Expression).ToString(); - - string? eventName = null; - - if (ma.ArgumentList?.Arguments is { Count: > 3 } args) - { - arg = args[3]; - eventName = semanticModel.GetConstantValue(arg.Expression).ToString(); - } - - var lm = new LoggerMethod - { - Name = method.Identifier.ToString(), - EventId = eventId, - Level = level, - Message = message, - EventName = eventName, - MessageHasTemplates = HasTemplates(message), - Documentation = GetDocs(method), - }; - lc.Methods.Add(lm); - - if (lm.Name.StartsWith("__", StringComparison.Ordinal)) - { - // can't have logging method names that start with __ since that can lead to conflicting symbol names - // because the generated symbols start with __ - Diag(ErrorInvalidMethodName, method.Identifier.GetLocation()); - } - - if (!GetSemanticModel(method.ReturnType.SyntaxTree).GetTypeInfo(method.ReturnType!).Type!.Equals(voidSymbol, SymbolEqualityComparer.Default)) - { - Diag(ErrorInvalidMethodReturnType, method.ReturnType.GetLocation()); - } - - if (method.Arity > 0) - { - Diag(ErrorMethodGeneric, method.GetLocation()); - } - - // ensure there are no duplicate ids. - if (ids.Contains(lm.EventId)) - { - Diag(ErrorEventIdReuse, ma.ArgumentList!.Arguments[0].GetLocation(), lm.EventId); - } - else - { - ids.Add(lm.EventId); - } - - if (string.IsNullOrWhiteSpace(lm.Message)) - { - Diag(ErrorInvalidMessage, ma.GetLocation(), method.Identifier.ToString()); - } - - foreach (var p in method.ParameterList.Parameters) - { - var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; - - var lp = new LoggerParameter - { - Name = p.Identifier.ToString(), - Type = p.Type!.ToString(), - IsExceptionType = IsBaseOrIdentity(pSymbol, exSymbol), - }; + Name = p.Identifier.ToString(), + Type = p.Type!.ToString(), + IsExceptionType = IsBaseOrIdentity(pSymbol, exSymbol), + }; - if (lp.Type.EndsWith("ILogger", StringComparison.Ordinal)) - { - continue; - } - - lm.Parameters.Add(lp); - - if (lp.Name.StartsWith("__", StringComparison.Ordinal)) - { - // can't have logging method parameter names that start with __ since that can lead to conflicting symbol names - // because all generated symbols start with __ - Diag(ErrorInvalidParameterName, p.Identifier.GetLocation()); - } - } - } + if (lp.Type.EndsWith("ILogger", StringComparison.Ordinal)) + { + continue; + } + + lm.Parameters.Add(lp); + + if (lp.Name.StartsWith("__", StringComparison.Ordinal)) + { + // can't have logging method parameter names that start with __ since that can lead to conflicting symbol names + // because all generated symbols start with __ + Diag(ErrorInvalidParameterName, p.Identifier.GetLocation()); } } } - - yield return lc; } } } + + yield return lc; } } @@ -327,21 +247,6 @@ private void Diag(DiagnosticDescriptor desc, Location? location, params object?[ _context.ReportDiagnostic(Diagnostic.Create(desc, location, messageArgs)); } - private static string GetDocs(SyntaxNode _) - { -#if false - This is not ready yet - foreach (var trivia in node.GetLeadingTrivia().Where(x => x.HasStructure)) - { - if (trivia.GetStructure() is DocumentationCommentTriviaSyntax sTrivia) - { - return sTrivia.ToString(); - } - } -#endif - return string.Empty; - } - // Workaround for https://github.com/dotnet/roslyn/pull/49330 private SemanticModel GetSemanticModel(SyntaxTree syntaxTree) { @@ -402,11 +307,7 @@ private class LoggerClass { public string? Namespace; public string Name = string.Empty; - public string OriginalInterfaceName = string.Empty; - public string AccessModifiers = string.Empty; public List Methods = new(); - public string Documentation = string.Empty; - public bool IsInterface { get; set; } } // A log method in a logging class @@ -419,7 +320,6 @@ private class LoggerMethod public string? EventName = null!; public List Parameters = new(); public bool MessageHasTemplates; - public string Documentation = string.Empty; } // A single parameter to a log method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 148387e24ffd8..43db4fb963c49 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -31,7 +31,7 @@ public void Execute(GeneratorExecutionContext context) var p = new Parser(context); var types = new StringBuilder(); - foreach (var lc in p.GetLogClasses(receiver.TypeDeclarations)) + foreach (var lc in p.GetLogClasses(receiver.ClassDeclarations)) { types.Append(GenType(lc)); } @@ -57,7 +57,7 @@ private static string GenType(LoggerClass lc) methods.Append(GenStruct(lm)); } methods.Append(GenEventId(lm)); - methods.Append(GenExtensionLogMethod(lc, lm)); + methods.Append(GenExtensionLogMethod(lm)); } var namespaceStart = string.Empty; @@ -71,41 +71,14 @@ private static string GenType(LoggerClass lc) return $@" {namespaceStart} - {lc.Documentation} - {lc.AccessModifiers} {(lc.IsInterface ? "static" : "")} class {lc.Name} + partial class {lc.Name} {{ {methods} - {GenWrapper(lc)} }} {namespaceEnd} "; } - private static string GenWrapper(LoggerClass lc) - { - if (!lc.IsInterface) - { - return ""; - } - var methods = new StringBuilder(); - foreach (var lm in lc.Methods) - { - methods.Append(GenInstanceLogMethod(lm)); - } - - return $@" - public static {lc.OriginalInterfaceName} Wrap(this ILogger logger) => new __Wrapper__(logger); - - private sealed class __Wrapper__ : {lc.OriginalInterfaceName} - {{ - private readonly ILogger __logger; - - public __Wrapper__(ILogger logger) => __logger = logger; - {methods} - }} -"; - } - private static string GenStruct(LoggerMethod lm) { var constructor = $@" @@ -160,14 +133,7 @@ private static string GenEventId(LoggerMethod lm) return $" private static readonly EventId __{lm.Name}EventId__ = new({lm.EventId}, " + (lm.EventName is null ? $"nameof({lm.Name})" : $"\"{lm.EventName}\"") + ");\n"; } - private static string GenInstanceLogMethod(LoggerMethod lm) - { - return $@" - public void {lm.Name}({GenParameters(lm)}) => __logger.{lm.Name}({GenArguments(lm)}); -"; - } - - private static string GenExtensionLogMethod(LoggerClass lc, LoggerMethod lm) + private static string GenExtensionLogMethod(LoggerMethod lm) { string exceptionArg = "null"; foreach (var p in lm.Parameters) @@ -179,7 +145,7 @@ private static string GenExtensionLogMethod(LoggerClass lc, LoggerMethod lm) } } - var loggerArg = lc.IsInterface ? "this ILogger logger" : "ILogger logger"; + var loggerArg = "ILogger logger"; var ctorCall = $"__{lm.Name}Struct__({ GenArguments(lm)})"; if (lm.Parameters.Count == 0) @@ -189,13 +155,12 @@ private static string GenExtensionLogMethod(LoggerClass lc, LoggerMethod lm) } return $@" - {lm.Documentation} - public static {(lc.IsInterface ? "" : "partial")} void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + public static partial void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (logger.IsEnabled((LogLevel){lm.Level})) {{ - var message = new {ctorCall}; - logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, message, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.ToString()" : "\"" + lm.Message + "\"")}); + var __state = new {ctorCall}; + logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, __state, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.ToString()" : "\"" + lm.Message + "\"")}); }} }} "; @@ -294,19 +259,14 @@ private static string GenCases(LoggerMethod lm) private sealed class SyntaxReceiver : ISyntaxReceiver { - public List TypeDeclarations { get; } = new(); + public List ClassDeclarations { get; } = new(); public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { - if (syntaxNode is InterfaceDeclarationSyntax interfaceSyntax && interfaceSyntax.AttributeLists.Count > 0) - { - TypeDeclarations.Add(interfaceSyntax); - } - // Any partial class - if (syntaxNode is ClassDeclarationSyntax { Modifiers: { Count: > 0 } modifiers } classSyntax && modifiers.Any(SyntaxKind.StaticKeyword) && modifiers.Any(SyntaxKind.PartialKeyword)) + if (syntaxNode is ClassDeclarationSyntax { Modifiers: { Count: > 0 } modifiers } classSyntax && modifiers.Any(SyntaxKind.PartialKeyword)) { - TypeDeclarations.Add(classSyntax); + ClassDeclarations.Add(classSyntax); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs index 2ff8dc516c877..0e1a6326a3e98 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs @@ -6,47 +6,46 @@ using Microsoft.Extensions.Logging; using Xunit; -// Used to test interfaces outside of a namespace -[LoggerExtensions] -interface ILoggerExtensionsNoNamespace +#pragma warning disable CA1801 // Review unused parameters + +// Used to test use outside of a namespace +partial class LoggerExtensionsNoNamespace { [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - void CouldNotOpenSocketNoNamespace(string hostName); + public static partial void CouldNotOpenSocketNoNamespace(ILogger logger, string hostName); } namespace Microsoft.Extensions.Logging.Generators.Tests { - // used to test interfaces inside a namespace - [LoggerExtensions] - interface ILoggerExtensions + // used to test use inside a namespace + partial class LoggerExtensions { [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - void CouldNotOpenSocket(string hostName); + public static partial void CouldNotOpenSocket(ILogger logger, string hostName); } - [LoggerExtensions] - interface IArgTestExtensions + partial class ArgTestExtensions { [LoggerMessage(0, LogLevel.Error, "M1")] - void Method1(); + public static partial void Method1(ILogger logger); [LoggerMessage(1, LogLevel.Error, "M2")] - void Method2(string p1); + public static partial void Method2(ILogger logger, string p1); [LoggerMessage(2, LogLevel.Error, "M3 {p1} {p2}")] - void Method3(string p1, int p2); + public static partial void Method3(ILogger logger, string p1, int p2); [LoggerMessage(3, LogLevel.Error, "M4")] - void Method4(InvalidOperationException p1); + public static partial void Method4(ILogger logger, InvalidOperationException p1); [LoggerMessage(4, LogLevel.Error, "M5")] - void Method5(InvalidOperationException p1, InvalidOperationException p2); + public static partial void Method5(ILogger logger, InvalidOperationException p1, InvalidOperationException p2); [LoggerMessage(5, LogLevel.Error, "M6")] - void Method6(InvalidOperationException p1, int p2); + public static partial void Method6(ILogger logger, InvalidOperationException p1, int p2); [LoggerMessage(6, LogLevel.Error, "M7")] - void Method7(int p1, InvalidOperationException p2); + public static partial void Method7(ILogger logger, int p1, InvalidOperationException p2); } public class LoggingTests @@ -57,52 +56,14 @@ public void ExtensionMethodTest() var logger = new MockLogger(); logger.Reset(); - logger.CouldNotOpenSocket("microsoft.com"); - Assert.Equal(LogLevel.Critical, logger.LastLogLevel); - Assert.Null(logger.LastException); - Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - logger.Reset(); - logger.CouldNotOpenSocketNoNamespace("microsoft.com"); - Assert.Equal(LogLevel.Critical, logger.LastLogLevel); - Assert.Null(logger.LastException); - Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - // same as above, but this time with explicit type references rather than extension method syntax so we can vouch for the namespaces being used - - logger.Reset(); - global::Microsoft.Extensions.Logging.Generators.Tests.LoggerExtensions.CouldNotOpenSocket(logger, "microsoft.com"); - Assert.Equal(LogLevel.Critical, logger.LastLogLevel); - Assert.Null(logger.LastException); - Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - logger.Reset(); - global::LoggerExtensionsNoNamespace.CouldNotOpenSocketNoNamespace(logger, "microsoft.com"); - Assert.Equal(LogLevel.Critical, logger.LastLogLevel); - Assert.Null(logger.LastException); - Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - } - - [Fact] - public void WrapperTypeTest() - { - var logger = new MockLogger(); - - logger.Reset(); - var d = global::Microsoft.Extensions.Logging.Generators.Tests.LoggerExtensions.Wrap(logger); // make sure this is using the right namespace - d.CouldNotOpenSocket("microsoft.com"); + LoggerExtensions.CouldNotOpenSocket(logger, "microsoft.com"); Assert.Equal(LogLevel.Critical, logger.LastLogLevel); Assert.Null(logger.LastException); Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); - var d2 = global::LoggerExtensionsNoNamespace.Wrap(logger); // make sure this is outside of any namespace - d2.CouldNotOpenSocketNoNamespace("microsoft.com"); + LoggerExtensionsNoNamespace.CouldNotOpenSocketNoNamespace(logger, "microsoft.com"); Assert.Equal(LogLevel.Critical, logger.LastLogLevel); Assert.Null(logger.LastException); Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); @@ -116,11 +77,7 @@ public void EnableTest() logger.Reset(); logger.Enabled = false; - var d = LoggerExtensions.Wrap(logger); - d.CouldNotOpenSocket("microsoft.com"); - Assert.Equal(0, logger.CallCount); // ensure the logger doesn't get called when it is disabled - - logger.CouldNotOpenSocket("microsoft.com"); + LoggerExtensions.CouldNotOpenSocket(logger, "microsoft.com"); Assert.Equal(0, logger.CallCount); // ensure the logger doesn't get called when it is disabled } @@ -128,49 +85,6 @@ public void EnableTest() public void ArgTest() { var logger = new MockLogger(); - var d = ArgTestExtensions.Wrap(logger); - - logger.Reset(); - d.Method1(); - Assert.Null(logger.LastException); - Assert.Equal("M1", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - logger.Reset(); - d.Method2("arg1"); - Assert.Null(logger.LastException); - Assert.Equal("M2", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - logger.Reset(); - d.Method3("arg1", 2); - Assert.Null(logger.LastException); - Assert.Equal("M3 arg1 2", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - logger.Reset(); - d.Method4(new InvalidOperationException("A")); -// Assert.Equal("A", logger.LastException!.Message); - Assert.Equal("M4", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - logger.Reset(); - d.Method5(new InvalidOperationException("A"), new InvalidOperationException("B")); -// Assert.Equal("A", logger.LastException!.Message); - Assert.Equal("M5", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - logger.Reset(); - d.Method6(new InvalidOperationException("A"), 2); -// Assert.Equal("A", logger.LastException!.Message); - Assert.Equal("M6", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); - - logger.Reset(); - d.Method7(1, new InvalidOperationException("B")); -// Assert.Equal("B", logger.LastException!.Message); - Assert.Equal("M7", logger.LastFormattedString); - Assert.Equal(1, logger.CallCount); logger.Reset(); ArgTestExtensions.Method1(logger); @@ -219,17 +133,16 @@ public void ArgTest() public void ReadOnlyListTest() { var logger = new MockLogger(); - var d = ArgTestExtensions.Wrap(logger); logger.Reset(); - d.Method1(); + ArgTestExtensions.Method1(logger); var rol = logger.LastState as IReadOnlyList>; Assert.Equal(0, rol!.Count); Assert.Empty(rol); Assert.Throws(() => _ = rol[0]); logger.Reset(); - d.Method2("arg1"); + ArgTestExtensions.Method2(logger, "arg1"); rol = logger.LastState as IReadOnlyList>; Assert.Equal(1, rol!.Count); #pragma warning disable CA1829 // Use Length/Count property instead of Count() when available @@ -240,7 +153,7 @@ public void ReadOnlyListTest() Assert.Throws(() => _ = rol[1]); logger.Reset(); - d.Method3("arg1", 2); + ArgTestExtensions.Method3(logger, "arg1", 2); rol = logger.LastState as IReadOnlyList>; Assert.Equal(2, rol!.Count); #pragma warning disable CA1829 // Use Length/Count property instead of Count() when available diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index feb5633d68857..d86bed3692d7c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -5,6 +5,7 @@ false enable true + true From d99cec0554234f6644eaa9ab9622fa234db93d1d Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Fri, 20 Nov 2020 10:31:28 -0800 Subject: [PATCH 019/121] Improve code generation by statically defining delegate types --- .../gen/LoggingGenerator.Parser.cs | 13 +++++---- .../gen/LoggingGenerator.cs | 27 ++++++++++++++++++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 07d31be976b38..414d8af102632 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -208,8 +208,16 @@ public IEnumerable GetLogClasses(List class Diag(ErrorInvalidMessage, ma.GetLocation(), method.Identifier.ToString()); } + bool first = true; foreach (var p in method.ParameterList.Parameters) { + if (first) + { + // skip the ILogger + first = false; + continue; + } + var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; var lp = new LoggerParameter @@ -219,11 +227,6 @@ public IEnumerable GetLogClasses(List class IsExceptionType = IsBaseOrIdentity(pSymbol, exSymbol), }; - if (lp.Type.EndsWith("ILogger", StringComparison.Ordinal)) - { - continue; - } - lm.Parameters.Add(lp); if (lp.Name.StartsWith("__", StringComparison.Ordinal)) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 43db4fb963c49..03a212440be49 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -4,6 +4,7 @@ namespace Microsoft.Extensions.Logging.Generators { using System; using System.Collections.Generic; + using System.Data; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -57,6 +58,7 @@ private static string GenType(LoggerClass lc) methods.Append(GenStruct(lm)); } methods.Append(GenEventId(lm)); + methods.Append(GenDelegate(lm)); methods.Append(GenExtensionLogMethod(lm)); } @@ -133,6 +135,16 @@ private static string GenEventId(LoggerMethod lm) return $" private static readonly EventId __{lm.Name}EventId__ = new({lm.EventId}, " + (lm.EventName is null ? $"nameof({lm.Name})" : $"\"{lm.EventName}\"") + ");\n"; } + private static string GenDelegate(LoggerMethod lm) + { + if (!lm.MessageHasTemplates) + { + return string.Empty; + } + + return $" private static readonly Func<__{lm.Name}Struct__, Exception?, string> __{lm.Name}Func__ = (s, _) => s.ToString();"; + } + private static string GenExtensionLogMethod(LoggerMethod lm) { string exceptionArg = "null"; @@ -154,13 +166,26 @@ private static string GenExtensionLogMethod(LoggerMethod lm) ctorCall = "Microsoft.Extensions.Logging.LogStateHolder()"; } + var formatCall = $"(s, _) => \"{ lm.Message}\""; + if (lm.MessageHasTemplates) + { + if (lm.Parameters.Count == 0) + { + formatCall = "Microsoft.Extensions.Logging.LogStateHolder.Format"; + } + else + { + formatCall = $"__{lm.Name}Func__"; + } + } + return $@" public static partial void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (logger.IsEnabled((LogLevel){lm.Level})) {{ var __state = new {ctorCall}; - logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, __state, {exceptionArg}, (s, _) => {(lm.MessageHasTemplates ? "s.ToString()" : "\"" + lm.Message + "\"")}); + logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, __state, {exceptionArg}, {formatCall}); }} }} "; From b31bca8f845c976f6c2e8ccfc4a67921c817c244 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Fri, 20 Nov 2020 11:04:42 -0800 Subject: [PATCH 020/121] Simplify generated code --- .../gen/LoggingGenerator.cs | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 03a212440be49..0156ffccac084 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -57,8 +57,6 @@ private static string GenType(LoggerClass lc) { methods.Append(GenStruct(lm)); } - methods.Append(GenEventId(lm)); - methods.Append(GenDelegate(lm)); methods.Append(GenExtensionLogMethod(lm)); } @@ -84,26 +82,29 @@ partial class {lc.Name} private static string GenStruct(LoggerMethod lm) { var constructor = $@" - public __{lm.Name}Struct__({GenParameters(lm)}) + public __{lm.Name}State({GenParameters(lm)}) {{ {GenFieldAssignments(lm)} }} "; - var format = string.Empty; - if (lm.MessageHasTemplates) - { - format = $@" + var format = $@" public override string ToString() => $""{lm.Message}""; "; + + var del = string.Empty; + if (lm.MessageHasTemplates) + { + del = $" public static readonly Func<__{lm.Name}State, Exception?, string> Format = (s, _) => s.ToString();"; } return $@" - private readonly struct __{lm.Name}Struct__ : IReadOnlyList> + private readonly struct __{lm.Name}State : IReadOnlyList> {{ {GenFields(lm)} {constructor} {format} +{del} public int Count => {lm.Parameters.Count}; @@ -130,21 +131,6 @@ public IEnumerator> GetEnumerator() "; } - private static string GenEventId(LoggerMethod lm) - { - return $" private static readonly EventId __{lm.Name}EventId__ = new({lm.EventId}, " + (lm.EventName is null ? $"nameof({lm.Name})" : $"\"{lm.EventName}\"") + ");\n"; - } - - private static string GenDelegate(LoggerMethod lm) - { - if (!lm.MessageHasTemplates) - { - return string.Empty; - } - - return $" private static readonly Func<__{lm.Name}Struct__, Exception?, string> __{lm.Name}Func__ = (s, _) => s.ToString();"; - } - private static string GenExtensionLogMethod(LoggerMethod lm) { string exceptionArg = "null"; @@ -158,12 +144,12 @@ private static string GenExtensionLogMethod(LoggerMethod lm) } var loggerArg = "ILogger logger"; - - var ctorCall = $"__{lm.Name}Struct__({ GenArguments(lm)})"; + + var ctorCall = $"new __{lm.Name}State({ GenArguments(lm)})"; if (lm.Parameters.Count == 0) { // when no parameters, use the common struct - ctorCall = "Microsoft.Extensions.Logging.LogStateHolder()"; + ctorCall = "new Microsoft.Extensions.Logging.LogStateHolder()"; } var formatCall = $"(s, _) => \"{ lm.Message}\""; @@ -175,17 +161,24 @@ private static string GenExtensionLogMethod(LoggerMethod lm) } else { - formatCall = $"__{lm.Name}Func__"; + formatCall = $"__{lm.Name}State.Format"; } } + var eventName = $"nameof({lm.Name})"; + if (lm.EventName != null) + { + eventName = $"\"{lm.EventName}\""; + } + + var eventIdCall = $"new EventId({lm.EventId}, {eventName})"; + return $@" public static partial void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (logger.IsEnabled((LogLevel){lm.Level})) {{ - var __state = new {ctorCall}; - logger.Log((LogLevel){lm.Level}, __{lm.Name}EventId__, __state, {exceptionArg}, {formatCall}); + logger.Log((LogLevel){lm.Level}, {eventIdCall}, {ctorCall}, {exceptionArg}, {formatCall}); }} }} "; From 1a4e33627bf3477c22a46aa04c1718cccf7e6a88 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 21 Nov 2020 10:58:16 -0800 Subject: [PATCH 021/121] Revamp code gen to shrink jitted size --- .../gen/LoggingGenerator.Parser.cs | 15 +- .../gen/LoggingGenerator.cs | 261 ++++++++++++------ .../LoggingTests.cs | 6 + 3 files changed, 193 insertions(+), 89 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 414d8af102632..cf2a16cf6c2b5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -220,12 +220,23 @@ public IEnumerable GetLogClasses(List class var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; + // BUGBUG: Terrible hack, need a real solution + var nspace = pSymbol.ContainingNamespace.ToString(); + var typeName = p.Type!.ToString(); +#pragma warning disable CA1308 // Normalize strings to uppercase + if (!string.IsNullOrWhiteSpace(pSymbol.ContainingNamespace.ToString()) && typeName.ToLowerInvariant() != typeName) +#pragma warning restore CA1308 // Normalize strings to uppercase + { + typeName = nspace + "." + typeName; + } + var lp = new LoggerParameter { Name = p.Identifier.ToString(), - Type = p.Type!.ToString(), + Type = typeName, IsExceptionType = IsBaseOrIdentity(pSymbol, exSymbol), - }; + + }; lm.Parameters.Add(lp); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 0156ffccac084..690d802138acd 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -5,7 +5,9 @@ namespace Microsoft.Extensions.Logging.Generators using System; using System.Collections.Generic; using System.Data; + using System.Globalization; using System.Text; + using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -14,6 +16,8 @@ namespace Microsoft.Extensions.Logging.Generators [Generator] public partial class LoggingGenerator : ISourceGenerator { + const int MaxStaeHolderArity = 6; + /// public void Initialize(GeneratorInitializationContext context) { @@ -37,15 +41,7 @@ public void Execute(GeneratorExecutionContext context) types.Append(GenType(lc)); } - var final = $@" -using System; -using System.Collections; -using System.Collections.Generic; -using Microsoft.Extensions.Logging; - -{types}"; - - context.AddSource(nameof(LoggingGenerator), SourceText.From(final, Encoding.UTF8)); + context.AddSource(nameof(LoggingGenerator), SourceText.From(types.ToString(), Encoding.UTF8)); } private static string GenType(LoggerClass lc) @@ -53,85 +49,187 @@ private static string GenType(LoggerClass lc) var methods = new StringBuilder(); foreach (var lm in lc.Methods) { - if (lm.Parameters.Count > 0) - { - methods.Append(GenStruct(lm)); - } - methods.Append(GenExtensionLogMethod(lm)); + methods.Append(GenStruct(lm)); + methods.Append(GenLogMethod(lm)); } - var namespaceStart = string.Empty; - var namespaceEnd = string.Empty; - - if (lc.Namespace != null) + if (string.IsNullOrWhiteSpace(lc.Namespace)) { - namespaceStart = $"namespace {lc.Namespace}\n{{\n"; - namespaceEnd = "}\n"; + return $@" + partial class {lc.Name} + {{ + {methods} + }} + "; } return $@" -{namespaceStart} - partial class {lc.Name} - {{ - {methods} - }} -{namespaceEnd} -"; + namespace {lc.Namespace} + {{ + partial class {lc.Name} + {{ + {methods} + }} + }} + "; } private static string GenStruct(LoggerMethod lm) { - var constructor = $@" - public __{lm.Name}State({GenParameters(lm)}) - {{ -{GenFieldAssignments(lm)} - }} -"; - - var format = $@" - public override string ToString() => $""{lm.Message}""; -"; - - var del = string.Empty; - if (lm.MessageHasTemplates) + if (lm.Parameters.Count == 0) { - del = $" public static readonly Func<__{lm.Name}State, Exception?, string> Format = (s, _) => s.ToString();"; + // we don't need a custom struct if there aren't any parameters + return string.Empty; } return $@" - private readonly struct __{lm.Name}State : IReadOnlyList> - {{ -{GenFields(lm)} -{constructor} -{format} -{del} - - public int Count => {lm.Parameters.Count}; - - public KeyValuePair this[int index] - {{ - get + private readonly struct __{lm.Name}State : global::System.Collections.Generic.IReadOnlyList> {{ - switch (index) + {GenHolderField(lm)} + + public __{lm.Name}State({GenParameters(lm)}) + {{ + {GenHolderFieldAssignment(lm)} + }} + + {GenFormatFunc(lm)} + + public override string ToString() {{ -{GenCases(lm)} - default: - throw new ArgumentOutOfRangeException(nameof(index)); + {GenToString(lm)} }} + + public int Count => {lm.Parameters.Count}; + public global::System.Collections.Generic.KeyValuePair this[int index] => _holder[index]; + public global::System.Collections.Generic.IEnumerator> GetEnumerator() => (global::System.Collections.Generic.IEnumerator>)_holder.GetEnumerator(); + System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); }} - }} + "; + } + + private static string GenHolderField(LoggerMethod lm) + { + if (lm.Parameters.Count > MaxStaeHolderArity) + { + return "private readonly global::System.Collections.Generic.KeyValuePair[] _holder;"; + } - public IEnumerator> GetEnumerator() - {{ -{GenEnumerator(lm)} - }} + var sb = new StringBuilder(); + foreach (var p in lm.Parameters) + { + if (sb.Length > 0) + { + sb.Append(", "); + } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - }} -"; + sb.Append(p.Type); + } + + return $"private readonly global::Microsoft.Extensions.Logging.LogStateHolder<{sb}> _holder;"; } - private static string GenExtensionLogMethod(LoggerMethod lm) + private static string GenHolderFieldAssignment(LoggerMethod lm) + { + if (lm.Parameters.Count == 1) + { + return $"_holder = new(nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name});"; + } + + var sb = new StringBuilder(); + + if (lm.Parameters.Count > MaxStaeHolderArity) + { + sb = new StringBuilder("_holder = new []{"); + + foreach (var p in lm.Parameters) + { + sb.Append($"new global::System.Collections.Generic.KeyValuePair(\"{p.Name}\", {p.Name}), "); + } + + sb.Append("};"); + } + else + { + sb.Append("_holder = new(new []{"); + bool first = true; + foreach (var p in lm.Parameters) + { + if (!first) + { + sb.Append(", "); + } + first = false; + sb.Append("nameof("); + sb.Append(p.Name); + sb.Append(')'); + } + + sb.Append("}, "); + first = true; + foreach (var p in lm.Parameters) + { + if (!first) + { + sb.Append(", "); + } + first = false; + + sb.Append(p.Name); + } + + sb.Append(");"); + }; + + return sb.ToString(); + } + + private static string GenFormatFunc(LoggerMethod lm) + { + if (lm.MessageHasTemplates) + { + return $"public static readonly global::System.Func<__{lm.Name}State, global::System.Exception?, string> Format = (s, _) => s.ToString();"; + } + + return string.Empty; + } + + private static string GenToString(LoggerMethod lm) + { + var sb = new StringBuilder(); + if (lm.Parameters.Count == 1) + { + sb.Append("var "); + sb.Append(lm.Parameters[0].Name); + sb.Append(" = _holder.Value;\n"); + } + else if (lm.Parameters.Count > MaxStaeHolderArity) + { + var index = 0; + foreach (var p in lm.Parameters) + { + sb.Append("var "); + sb.Append(p.Name); + sb.AppendFormat(CultureInfo.InvariantCulture, " = _holder[0];\n", index++); + } + } + else + { + var index = 1; + foreach (var p in lm.Parameters) + { + sb.Append("var "); + sb.Append(p.Name); + sb.AppendFormat(CultureInfo.InvariantCulture, " = _holder.Value{0};\n", index++); + } + } + + return $@" + {sb} + return $""{lm.Message}""; + "; + } + + private static string GenLogMethod(LoggerMethod lm) { string exceptionArg = "null"; foreach (var p in lm.Parameters) @@ -143,7 +241,7 @@ private static string GenExtensionLogMethod(LoggerMethod lm) } } - var loggerArg = "ILogger logger"; + var loggerArg = "global::Microsoft.Extensions.Logging.ILogger logger"; var ctorCall = $"new __{lm.Name}State({ GenArguments(lm)})"; if (lm.Parameters.Count == 0) @@ -171,17 +269,17 @@ private static string GenExtensionLogMethod(LoggerMethod lm) eventName = $"\"{lm.EventName}\""; } - var eventIdCall = $"new EventId({lm.EventId}, {eventName})"; + var eventIdCall = $"new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName})"; return $@" - public static partial void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) - {{ - if (logger.IsEnabled((LogLevel){lm.Level})) - {{ - logger.Log((LogLevel){lm.Level}, {eventIdCall}, {ctorCall}, {exceptionArg}, {formatCall}); - }} - }} -"; + public static partial void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {{ + if (logger.IsEnabled((global::Microsoft.Extensions.Logging.LogLevel){lm.Level})) + {{ + logger.Log((global::Microsoft.Extensions.Logging.LogLevel){lm.Level}, {eventIdCall}, {ctorCall}, {exceptionArg}, {formatCall}); + }} + }} + "; } private static string GenParameters(LoggerMethod lm) @@ -228,17 +326,6 @@ private static string GenArguments(LoggerMethod lm) return sb.ToString(); } - private static string GenFields(LoggerMethod lm) - { - var sb = new StringBuilder(); - foreach (var p in lm.Parameters) - { - sb.Append($" private readonly {p.Type} {p.Name};\n"); - } - - return sb.ToString(); - } - private static string GenFieldAssignments(LoggerMethod lm) { var sb = new StringBuilder(); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs index 0e1a6326a3e98..fcb01d747d61a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs @@ -46,6 +46,12 @@ partial class ArgTestExtensions [LoggerMessage(6, LogLevel.Error, "M7")] public static partial void Method7(ILogger logger, int p1, InvalidOperationException p2); + + [LoggerMessage(7, LogLevel.Error, "M8")] + public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); + + [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] + public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); } public class LoggingTests From 128f5d2b8dd4c7da9aa4a7200a2e5874e4747633 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 21 Nov 2020 21:15:08 -0800 Subject: [PATCH 022/121] Bunch of improvements. - Add error checking to ensure the first argument to a logging method implements the ILogger interface. - Can now specify logging methods with generic ILogger as logger. - Add error checking to prevent generic logging method parameters. - Add error checking to ensure logging methods are static and partial - Can now specify logging methods which different access modifiers. - Eliminate or reduce cascading errors in many cases. - Ensure generated symbol names all start with __ so as not to conflict with user-specified symbols --- .../gen/LoggingGenerator.Parser.cs | 121 ++++++++++++++++-- .../gen/LoggingGenerator.cs | 28 ++-- 2 files changed, 123 insertions(+), 26 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index cf2a16cf6c2b5..4c36d343c2bea 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -73,14 +73,46 @@ private class Parser DiagnosticSeverity.Error, isEnabledByDefault: true); - private static readonly DiagnosticDescriptor ErrorMethodGeneric = new( + private static readonly DiagnosticDescriptor ErrorFirstArgMustBeILogger = new( id: "LG7", + title: "The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface", + messageFormat: "The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorNotStaticMethod = new( + id: "LG8", + title: "Logging methods must be static", + messageFormat: "Logging methods must be static", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorNotPartialMethod = new( + id: "LG9", + title: "Logging methods must be partial", + messageFormat: "Logging methods must be partial", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor ErrorMethodIsGeneric = new( + id: "LG10", title: "Logging methods cannot be generic", messageFormat: "Logging methods cannot be generic", category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); + private static readonly DiagnosticDescriptor ErrorParameterIsGeneric = new( + id: "LG11", + title: "Logging method parameters cannot be generic", + messageFormat: "Logging method parameters cannot be generic", + category: DiagnosticCategory, + DiagnosticSeverity.Error, + isEnabledByDefault: true); + private readonly Compilation _compilation; private readonly GeneratorExecutionContext _context; private readonly Dictionary _semanticModels = new(); @@ -117,6 +149,13 @@ public IEnumerable GetLogClasses(List class yield break; } + var loggerSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILogger"); + if (loggerSymbol == null) + { + Diag(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.ILogger"); + yield break; + } + foreach (var classDef in classes) { // determine the namespace the class is declared in, if any @@ -128,6 +167,7 @@ public IEnumerable GetLogClasses(List class { // since this generator doesn't know how to generate a nested type... Diag(ErrorNestedType, classDef.Identifier.GetLocation()); + continue; } } @@ -173,9 +213,10 @@ public IEnumerable GetLogClasses(List class Message = message, EventName = eventName, MessageHasTemplates = HasTemplates(message), + Modifier = string.Empty, }; - lc.Methods.Add(lm); + bool keep = true; if (lm.Name.StartsWith("__", StringComparison.Ordinal)) { // can't have logging method names that start with __ since that can lead to conflicting symbol names @@ -186,11 +227,47 @@ public IEnumerable GetLogClasses(List class if (!GetSemanticModel(method.ReturnType.SyntaxTree).GetTypeInfo(method.ReturnType!).Type!.Equals(voidSymbol, SymbolEqualityComparer.Default)) { Diag(ErrorInvalidMethodReturnType, method.ReturnType.GetLocation()); + keep = false; } if (method.Arity > 0) { - Diag(ErrorMethodGeneric, method.GetLocation()); + Diag(ErrorMethodIsGeneric, method.Identifier.GetLocation()); + keep = false; + } + + bool isStatic = false; + bool isPartial = false; + foreach (var mod in method.Modifiers) + { + switch (mod.Text) + { + case "static": + isStatic = true; + break; + + case "partial": + isPartial = true; + break; + + case "public": + case "internal": + case "private": + lm.Modifier = mod.Text; + break; + } + } + + if (!isStatic) + { + Diag(ErrorNotStaticMethod, method.GetLocation()); + keep = false; + } + + if (!isPartial) + { + Diag(ErrorNotPartialMethod, method.GetLocation()); + keep = false; } // ensure there are no duplicate ids. @@ -211,13 +288,6 @@ public IEnumerable GetLogClasses(List class bool first = true; foreach (var p in method.ParameterList.Parameters) { - if (first) - { - // skip the ILogger - first = false; - continue; - } - var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; // BUGBUG: Terrible hack, need a real solution @@ -230,13 +300,27 @@ public IEnumerable GetLogClasses(List class typeName = nspace + "." + typeName; } + if (first) + { + // skip the ILogger + first = false; + + if (!IsBaseOrIdentity(pSymbol, loggerSymbol)) + { + Diag(ErrorFirstArgMustBeILogger, p.Identifier.GetLocation()); + keep = false; + } + lm.LoggerType = typeName; + + continue; + } + var lp = new LoggerParameter { Name = p.Identifier.ToString(), Type = typeName, IsExceptionType = IsBaseOrIdentity(pSymbol, exSymbol), - - }; + }; lm.Parameters.Add(lp); @@ -246,6 +330,17 @@ public IEnumerable GetLogClasses(List class // because all generated symbols start with __ Diag(ErrorInvalidParameterName, p.Identifier.GetLocation()); } + + if (pSymbol.TypeKind == TypeKind.TypeParameter) + { + Diag(ErrorParameterIsGeneric, p.Identifier.GetLocation()); + keep = false; + } + } + + if (keep) + { + lc.Methods.Add(lm); } } } @@ -334,6 +429,8 @@ private class LoggerMethod public string? EventName = null!; public List Parameters = new(); public bool MessageHasTemplates; + public string Modifier = string.Empty; + public string LoggerType = string.Empty; } // A single parameter to a log method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 690d802138acd..9c8eada1c8dd8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -100,8 +100,8 @@ public override string ToString() }} public int Count => {lm.Parameters.Count}; - public global::System.Collections.Generic.KeyValuePair this[int index] => _holder[index]; - public global::System.Collections.Generic.IEnumerator> GetEnumerator() => (global::System.Collections.Generic.IEnumerator>)_holder.GetEnumerator(); + public global::System.Collections.Generic.KeyValuePair this[int index] => __holder[index]; + public global::System.Collections.Generic.IEnumerator> GetEnumerator() => (global::System.Collections.Generic.IEnumerator>)__holder.GetEnumerator(); System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); }} "; @@ -111,7 +111,7 @@ private static string GenHolderField(LoggerMethod lm) { if (lm.Parameters.Count > MaxStaeHolderArity) { - return "private readonly global::System.Collections.Generic.KeyValuePair[] _holder;"; + return "private readonly global::System.Collections.Generic.KeyValuePair[] __holder;"; } var sb = new StringBuilder(); @@ -125,21 +125,21 @@ private static string GenHolderField(LoggerMethod lm) sb.Append(p.Type); } - return $"private readonly global::Microsoft.Extensions.Logging.LogStateHolder<{sb}> _holder;"; + return $"private readonly global::Microsoft.Extensions.Logging.LogStateHolder<{sb}> __holder;"; } private static string GenHolderFieldAssignment(LoggerMethod lm) { if (lm.Parameters.Count == 1) { - return $"_holder = new(nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name});"; + return $"__holder = new(nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name});"; } var sb = new StringBuilder(); if (lm.Parameters.Count > MaxStaeHolderArity) { - sb = new StringBuilder("_holder = new []{"); + sb = new StringBuilder("__holder = new []{"); foreach (var p in lm.Parameters) { @@ -150,7 +150,7 @@ private static string GenHolderFieldAssignment(LoggerMethod lm) } else { - sb.Append("_holder = new(new []{"); + sb.Append("__holder = new(new []{"); bool first = true; foreach (var p in lm.Parameters) { @@ -200,7 +200,7 @@ private static string GenToString(LoggerMethod lm) { sb.Append("var "); sb.Append(lm.Parameters[0].Name); - sb.Append(" = _holder.Value;\n"); + sb.Append(" = __holder.Value;\n"); } else if (lm.Parameters.Count > MaxStaeHolderArity) { @@ -209,7 +209,7 @@ private static string GenToString(LoggerMethod lm) { sb.Append("var "); sb.Append(p.Name); - sb.AppendFormat(CultureInfo.InvariantCulture, " = _holder[0];\n", index++); + sb.AppendFormat(CultureInfo.InvariantCulture, " = __holder[0];\n", index++); } } else @@ -219,7 +219,7 @@ private static string GenToString(LoggerMethod lm) { sb.Append("var "); sb.Append(p.Name); - sb.AppendFormat(CultureInfo.InvariantCulture, " = _holder.Value{0};\n", index++); + sb.AppendFormat(CultureInfo.InvariantCulture, " = __holder.Value{0};\n", index++); } } @@ -241,7 +241,7 @@ private static string GenLogMethod(LoggerMethod lm) } } - var loggerArg = "global::Microsoft.Extensions.Logging.ILogger logger"; + var loggerArg = $"{lm.LoggerType} __logger"; var ctorCall = $"new __{lm.Name}State({ GenArguments(lm)})"; if (lm.Parameters.Count == 0) @@ -272,11 +272,11 @@ private static string GenLogMethod(LoggerMethod lm) var eventIdCall = $"new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName})"; return $@" - public static partial void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {lm.Modifier} static partial void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ - if (logger.IsEnabled((global::Microsoft.Extensions.Logging.LogLevel){lm.Level})) + if (__logger.IsEnabled((global::Microsoft.Extensions.Logging.LogLevel){lm.Level})) {{ - logger.Log((global::Microsoft.Extensions.Logging.LogLevel){lm.Level}, {eventIdCall}, {ctorCall}, {exceptionArg}, {formatCall}); + __logger.Log((global::Microsoft.Extensions.Logging.LogLevel){lm.Level}, {eventIdCall}, {ctorCall}, {exceptionArg}, {formatCall}); }} }} "; From 3740b68af20947d06db458502ace479ce890817a Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 21 Nov 2020 22:19:34 -0800 Subject: [PATCH 023/121] Enable localization of error messages --- .../gen/LoggingGenerator.Parser.cs | 52 ++-- .../gen/LoggingGeneratorResources.Designer.cs | 279 ++++++++++++++++++ .../gen/LoggingGeneratorResources.resx | 205 +++++++++++++ ...osoft.Extensions.Logging.Generators.csproj | 15 + 4 files changed, 527 insertions(+), 24 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 4c36d343c2bea..a5a52a576e369 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -1,14 +1,18 @@ // © Microsoft Corporation. All rights reserved. +[assembly: System.Resources.NeutralResourcesLanguage("en-us")] + namespace Microsoft.Extensions.Logging.Generators { using System; using System.Collections.Generic; using System.Linq; + using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; + public partial class LoggingGenerator { private class Parser @@ -19,96 +23,96 @@ private class Parser private static readonly DiagnosticDescriptor ErrorInvalidMethodName = new( id: "LG0", - title: "Logging method names cannot start with __", - messageFormat: "Logging method names cannot start with __", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMethodNameTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMethodNameMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidMessage = new( id: "LG1", - title: "Missing message for logging method", - messageFormat: "Missing message for logging method {0}", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMessageTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMessageMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidParameterName = new( id: "LG2", - title: "Logging method parameter names cannot start with __", - messageFormat: "Logging method parameter names cannot start with __", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidParameterNameTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidParameterNameMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNestedType = new( id: "LG3", - title: "Logging class cannot be in nested types", - messageFormat: "Logging class cannot be in nested types", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNestedTypeTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNestedTypeMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorMissingRequiredType = new( id: "LG4", - title: "Could not find a required type definition", - messageFormat: "Could not find definition for type {0}", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorMissingRequiredTypeTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorMissingRequiredTypeMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorEventIdReuse = new( id: "LG5", - title: "Multiple logging messages cannot use the same event id", - messageFormat: "Multiple logging messages are using event id {0}", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorEventIdReuseTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorEventIdReuseMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidMethodReturnType = new( id: "LG6", - title: "Logging methods must return void", - messageFormat: "Logging methods must return void", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMethodReturnTypeTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMethodReturnTypeMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorFirstArgMustBeILogger = new( id: "LG7", - title: "The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface", - messageFormat: "The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorFirstArgMustBeILoggerTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorFirstArgMustBeILoggerMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNotStaticMethod = new( id: "LG8", - title: "Logging methods must be static", - messageFormat: "Logging methods must be static", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNotStaticMethodTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNotStaticMethodMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNotPartialMethod = new( id: "LG9", - title: "Logging methods must be partial", - messageFormat: "Logging methods must be partial", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNotPartialMethodTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNotPartialMethodMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorMethodIsGeneric = new( id: "LG10", - title: "Logging methods cannot be generic", - messageFormat: "Logging methods cannot be generic", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorMethodIsGenericTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorMethodIsGenericMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorParameterIsGeneric = new( id: "LG11", - title: "Logging method parameters cannot be generic", - messageFormat: "Logging method parameters cannot be generic", + title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorParameterIsGenericTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorParameterIsGenericMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs new file mode 100644 index 0000000000000..546c8f63aa67a --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs @@ -0,0 +1,279 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Extensions.Logging.Generators { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class LoggingGeneratorResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal LoggingGeneratorResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Extensions.Logging.Generators.LoggingGeneratorResources", typeof(LoggingGeneratorResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Multiple logging messages are using event id {0}. + /// + internal static string ErrorEventIdReuseMessage { + get { + return ResourceManager.GetString("ErrorEventIdReuseMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Multiple logging messages cannot use the same event id. + /// + internal static string ErrorEventIdReuseTitle { + get { + return ResourceManager.GetString("ErrorEventIdReuseTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. + /// + internal static string ErrorFirstArgMustBeILoggerMessage { + get { + return ResourceManager.GetString("ErrorFirstArgMustBeILoggerMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. + /// + internal static string ErrorFirstArgMustBeILoggerTitle { + get { + return ResourceManager.GetString("ErrorFirstArgMustBeILoggerTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Missing message for logging method {0}. + /// + internal static string ErrorInvalidMessageMessage { + get { + return ResourceManager.GetString("ErrorInvalidMessageMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Missing message for logging method. + /// + internal static string ErrorInvalidMessageTitle { + get { + return ResourceManager.GetString("ErrorInvalidMessageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging method names cannot start with __. + /// + internal static string ErrorInvalidMethodNameMessage { + get { + return ResourceManager.GetString("ErrorInvalidMethodNameMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging method names cannot start with __. + /// + internal static string ErrorInvalidMethodNameTitle { + get { + return ResourceManager.GetString("ErrorInvalidMethodNameTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods must return void. + /// + internal static string ErrorInvalidMethodReturnTypeMessage { + get { + return ResourceManager.GetString("ErrorInvalidMethodReturnTypeMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods must return void. + /// + internal static string ErrorInvalidMethodReturnTypeTitle { + get { + return ResourceManager.GetString("ErrorInvalidMethodReturnTypeTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging method parameter names cannot start with __. + /// + internal static string ErrorInvalidParameterNameMessage { + get { + return ResourceManager.GetString("ErrorInvalidParameterNameMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging method parameter names cannot start with __. + /// + internal static string ErrorInvalidParameterNameTitle { + get { + return ResourceManager.GetString("ErrorInvalidParameterNameTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods cannot be generic. + /// + internal static string ErrorMethodIsGenericMessage { + get { + return ResourceManager.GetString("ErrorMethodIsGenericMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods cannot be generic. + /// + internal static string ErrorMethodIsGenericTitle { + get { + return ResourceManager.GetString("ErrorMethodIsGenericTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not find definition for type {0}. + /// + internal static string ErrorMissingRequiredTypeMessage { + get { + return ResourceManager.GetString("ErrorMissingRequiredTypeMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not find a required type definition. + /// + internal static string ErrorMissingRequiredTypeTitle { + get { + return ResourceManager.GetString("ErrorMissingRequiredTypeTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging class cannot be in nested types. + /// + internal static string ErrorNestedTypeMessage { + get { + return ResourceManager.GetString("ErrorNestedTypeMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging class cannot be in nested types. + /// + internal static string ErrorNestedTypeTitle { + get { + return ResourceManager.GetString("ErrorNestedTypeTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods must be partial. + /// + internal static string ErrorNotPartialMethodMessage { + get { + return ResourceManager.GetString("ErrorNotPartialMethodMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods must be partial. + /// + internal static string ErrorNotPartialMethodTitle { + get { + return ResourceManager.GetString("ErrorNotPartialMethodTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods must be static. + /// + internal static string ErrorNotStaticMethodMessage { + get { + return ResourceManager.GetString("ErrorNotStaticMethodMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods must be static. + /// + internal static string ErrorNotStaticMethodTitle { + get { + return ResourceManager.GetString("ErrorNotStaticMethodTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging method parameters cannot be generic. + /// + internal static string ErrorParameterIsGenericMessage { + get { + return ResourceManager.GetString("ErrorParameterIsGenericMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging method parameters cannot be generic. + /// + internal static string ErrorParameterIsGenericTitle { + get { + return ResourceManager.GetString("ErrorParameterIsGenericTitle", resourceCulture); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx new file mode 100644 index 0000000000000..5bece518a78ab --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Logging method names cannot start with __ + + + Logging method names cannot start with __ + + + + Missing message for logging method + + + Missing message for logging method {0} + + + + Logging method parameter names cannot start with __ + + + Logging method parameter names cannot start with __ + + + + Logging class cannot be in nested types + + + Logging class cannot be in nested types + + + + Could not find a required type definition + + + Could not find definition for type {0} + + + + Multiple logging messages cannot use the same event id + + + Multiple logging messages are using event id {0} + + + + Logging methods must return void + + + Logging methods must return void + + + + The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface + + + The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface + + + + Logging methods must be static + + + Logging methods must be static + + + + Logging methods must be partial + + + Logging methods must be partial + + + + Logging methods cannot be generic + + + Logging methods cannot be generic + + + + Logging method parameters cannot be generic + + + Logging method parameters cannot be generic + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 6bfaf04ac99e7..162e16d403033 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -14,4 +14,19 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + + + True + True + LoggingGeneratorResources.resx + + + + + + ResXFileCodeGenerator + LoggingGeneratorResources.Designer.cs + + From e275551413687ece4b4834751323aa720b35b6ad Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 08:21:39 -0800 Subject: [PATCH 024/121] Substantially reduce the size of the generated code --- .../gen/LoggingGenerator.cs | 262 ++++++------------ .../LoggingTests.cs | 12 +- 2 files changed, 90 insertions(+), 184 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 9c8eada1c8dd8..3060c2852ac78 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -6,6 +6,7 @@ namespace Microsoft.Extensions.Logging.Generators using System.Collections.Generic; using System.Data; using System.Globalization; + using System.Net.Mime; using System.Text; using System.Threading; using Microsoft.CodeAnalysis; @@ -49,7 +50,7 @@ private static string GenType(LoggerClass lc) var methods = new StringBuilder(); foreach (var lm in lc.Methods) { - methods.Append(GenStruct(lm)); + methods.Append(GenFormatFunc(lm)); methods.Append(GenLogMethod(lm)); } @@ -74,146 +75,50 @@ partial class {lc.Name} "; } - private static string GenStruct(LoggerMethod lm) - { - if (lm.Parameters.Count == 0) - { - // we don't need a custom struct if there aren't any parameters - return string.Empty; - } - - return $@" - private readonly struct __{lm.Name}State : global::System.Collections.Generic.IReadOnlyList> - {{ - {GenHolderField(lm)} - - public __{lm.Name}State({GenParameters(lm)}) - {{ - {GenHolderFieldAssignment(lm)} - }} - - {GenFormatFunc(lm)} - - public override string ToString() - {{ - {GenToString(lm)} - }} - - public int Count => {lm.Parameters.Count}; - public global::System.Collections.Generic.KeyValuePair this[int index] => __holder[index]; - public global::System.Collections.Generic.IEnumerator> GetEnumerator() => (global::System.Collections.Generic.IEnumerator>)__holder.GetEnumerator(); - System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - }} - "; - } - - private static string GenHolderField(LoggerMethod lm) + private static string GenFormatFunc(LoggerMethod lm) { - if (lm.Parameters.Count > MaxStaeHolderArity) - { - return "private readonly global::System.Collections.Generic.KeyValuePair[] __holder;"; - } - + string typeName; var sb = new StringBuilder(); - foreach (var p in lm.Parameters) - { - if (sb.Length > 0) - { - sb.Append(", "); - } - sb.Append(p.Type); + if (lm.Parameters.Count == 0 || !lm.MessageHasTemplates) + { + return string.Empty; } - - return $"private readonly global::Microsoft.Extensions.Logging.LogStateHolder<{sb}> __holder;"; - } - - private static string GenHolderFieldAssignment(LoggerMethod lm) - { - if (lm.Parameters.Count == 1) + else if (lm.Parameters.Count == 1) { - return $"__holder = new(nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name});"; + typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; + sb.Append("var "); + sb.Append(lm.Parameters[0].Name); + sb.Append(" = __holder.Value;\n"); } - - var sb = new StringBuilder(); - - if (lm.Parameters.Count > MaxStaeHolderArity) + else if (lm.Parameters.Count > MaxStaeHolderArity) { - sb = new StringBuilder("__holder = new []{"); - + typeName = $"global::System.Collections.Generic.KeyValuePair[]"; + var index = 0; foreach (var p in lm.Parameters) { - sb.Append($"new global::System.Collections.Generic.KeyValuePair(\"{p.Name}\", {p.Name}), "); + sb.Append("var "); + sb.Append(p.Name); + sb.AppendFormat(CultureInfo.InvariantCulture, " = __holder[{0}];\n", index++); } - - sb.Append("};"); } else { - sb.Append("__holder = new(new []{"); - bool first = true; - foreach (var p in lm.Parameters) - { - if (!first) - { - sb.Append(", "); - } - first = false; - sb.Append("nameof("); - sb.Append(p.Name); - sb.Append(')'); - } + sb.Append("global::Microsoft.Extensions.Logging.LogStateHolder<"); - sb.Append("}, "); - first = true; foreach (var p in lm.Parameters) { - if (!first) + if (p != lm.Parameters[0]) { sb.Append(", "); } - first = false; - sb.Append(p.Name); + sb.Append(p.Type); } + sb.Append('>'); + typeName = sb.ToString(); - sb.Append(");"); - }; - - return sb.ToString(); - } - - private static string GenFormatFunc(LoggerMethod lm) - { - if (lm.MessageHasTemplates) - { - return $"public static readonly global::System.Func<__{lm.Name}State, global::System.Exception?, string> Format = (s, _) => s.ToString();"; - } - - return string.Empty; - } - - private static string GenToString(LoggerMethod lm) - { - var sb = new StringBuilder(); - if (lm.Parameters.Count == 1) - { - sb.Append("var "); - sb.Append(lm.Parameters[0].Name); - sb.Append(" = __holder.Value;\n"); - } - else if (lm.Parameters.Count > MaxStaeHolderArity) - { - var index = 0; - foreach (var p in lm.Parameters) - { - sb.Append("var "); - sb.Append(p.Name); - sb.AppendFormat(CultureInfo.InvariantCulture, " = __holder[0];\n", index++); - } - } - else - { + sb.Clear(); var index = 1; foreach (var p in lm.Parameters) { @@ -223,10 +128,11 @@ private static string GenToString(LoggerMethod lm) } } - return $@" - {sb} - return $""{lm.Message}""; - "; + return $@"private static readonly global::System.Func<{typeName}, global::System.Exception?, string> __{lm.Name}FormatFunc = (__holder, _) => + {{ + {sb} + return $""{lm.Message}""; + }};"; } private static string GenLogMethod(LoggerMethod lm) @@ -243,14 +149,7 @@ private static string GenLogMethod(LoggerMethod lm) var loggerArg = $"{lm.LoggerType} __logger"; - var ctorCall = $"new __{lm.Name}State({ GenArguments(lm)})"; - if (lm.Parameters.Count == 0) - { - // when no parameters, use the common struct - ctorCall = "new Microsoft.Extensions.Logging.LogStateHolder()"; - } - - var formatCall = $"(s, _) => \"{ lm.Message}\""; + var formatCall = $"(_, _) => \"{ lm.Message}\""; if (lm.MessageHasTemplates) { if (lm.Parameters.Count == 0) @@ -259,7 +158,7 @@ private static string GenLogMethod(LoggerMethod lm) } else { - formatCall = $"__{lm.Name}State.Format"; + formatCall = $"__{lm.Name}FormatFunc"; } } @@ -276,7 +175,12 @@ private static string GenLogMethod(LoggerMethod lm) {{ if (__logger.IsEnabled((global::Microsoft.Extensions.Logging.LogLevel){lm.Level})) {{ - __logger.Log((global::Microsoft.Extensions.Logging.LogLevel){lm.Level}, {eventIdCall}, {ctorCall}, {exceptionArg}, {formatCall}); + __logger.Log( + (global::Microsoft.Extensions.Logging.LogLevel){lm.Level}, + {eventIdCall}, + {GenHolder(lm)}, + {exceptionArg}, + {formatCall}); }} }} "; @@ -284,11 +188,6 @@ private static string GenLogMethod(LoggerMethod lm) private static string GenParameters(LoggerMethod lm) { - if (lm.Parameters.Count == 0) - { - return string.Empty; - } - var sb = new StringBuilder(); foreach (var p in lm.Parameters) { @@ -305,61 +204,68 @@ private static string GenParameters(LoggerMethod lm) return sb.ToString(); } - private static string GenArguments(LoggerMethod lm) + private static string GenHolder(LoggerMethod lm) { if (lm.Parameters.Count == 0) { - return string.Empty; + return "new global::Microsoft.Extensions.Logging.LogStateHolder()"; } - - var sb = new StringBuilder(); - foreach (var p in lm.Parameters) + else if (lm.Parameters.Count == 1) { - if (sb.Length > 0) + return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>(nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name})"; + } + else if (lm.Parameters.Count > MaxStaeHolderArity) + { + var sb = new StringBuilder("new []{"); + foreach (var p in lm.Parameters) { - sb.Append(", "); + sb.Append($"new global::System.Collections.Generic.KeyValuePair(\"{p.Name}\", {p.Name}), "); } - sb.Append(p.Name); + sb.Append('}'); + return sb.ToString(); } - - return sb.ToString(); - } - - private static string GenFieldAssignments(LoggerMethod lm) - { - var sb = new StringBuilder(); - foreach (var p in lm.Parameters) + else { - sb.Append($" this.{p.Name} = {p.Name};\n"); - } + var sb = new StringBuilder(); + foreach (var p in lm.Parameters) + { + if (sb.Length > 0) + { + sb.Append(", "); + } - return sb.ToString(); - } + sb.Append(p.Type); + } + var tp = sb.ToString(); - private static string GenEnumerator(LoggerMethod lm) - { - var sb = new StringBuilder(); - int index = 0; - foreach (var p in lm.Parameters) - { - sb.Append($" yield return this[{index}];\n"); - } + sb.Clear(); + sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>(new []{{"); + foreach (var p in lm.Parameters) + { + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } + sb.Append("nameof("); + sb.Append(p.Name); + sb.Append(')'); + } - return sb.ToString(); - } + sb.Append("}, "); + foreach (var p in lm.Parameters) + { + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } - private static string GenCases(LoggerMethod lm) - { - var sb = new StringBuilder(); - var index = 0; - foreach (var p in lm.Parameters) - { - sb.Append($" case {index++}:\n"); - sb.Append($" return new KeyValuePair(nameof({p.Name}), {p.Name});\n"); - } + sb.Append(p.Name); + } - return sb.ToString(); + sb.Append(')'); + return sb.ToString(); + } } private sealed class SyntaxReceiver : ISyntaxReceiver diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs index fcb01d747d61a..8ecb425c2ef6b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs @@ -142,33 +142,33 @@ public void ReadOnlyListTest() logger.Reset(); ArgTestExtensions.Method1(logger); - var rol = logger.LastState as IReadOnlyList>; + var rol = logger.LastState as IReadOnlyList>; Assert.Equal(0, rol!.Count); Assert.Empty(rol); Assert.Throws(() => _ = rol[0]); logger.Reset(); ArgTestExtensions.Method2(logger, "arg1"); - rol = logger.LastState as IReadOnlyList>; + rol = logger.LastState as IReadOnlyList>; Assert.Equal(1, rol!.Count); #pragma warning disable CA1829 // Use Length/Count property instead of Count() when available Assert.Equal(1, rol.LongCount()); #pragma warning restore CA1829 // Use Length/Count property instead of Count() when available Assert.Equal("p1", (string)rol[0].Key); - Assert.Equal("arg1", (string)rol[0].Value); + Assert.Equal("arg1", (string?)rol[0].Value); Assert.Throws(() => _ = rol[1]); logger.Reset(); ArgTestExtensions.Method3(logger, "arg1", 2); - rol = logger.LastState as IReadOnlyList>; + rol = logger.LastState as IReadOnlyList>; Assert.Equal(2, rol!.Count); #pragma warning disable CA1829 // Use Length/Count property instead of Count() when available Assert.Equal(2, rol.LongCount()); #pragma warning restore CA1829 // Use Length/Count property instead of Count() when available Assert.Equal("p1", (string)rol[0].Key); - Assert.Equal("arg1", (string)rol[0].Value); + Assert.Equal("arg1", (string?)rol[0].Value); Assert.Equal("p2", (string)rol[1].Key); - Assert.Equal(2, (int)rol[1].Value); + Assert.Equal(2, (int?)rol[1].Value); Assert.Throws(() => _ = rol[2]); } } From fc8ce48f035f32e62b4e55125b1e67b07488f3a7 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 09:21:01 -0800 Subject: [PATCH 025/121] Add unit tests for Microsoft.Extensions.Logging.Attributes --- .../Microsoft.Extensions.Logging.Generators.Tests.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index d86bed3692d7c..14b8e4ecb1ce2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -2,7 +2,6 @@ net5.0 - false enable true true From 951f5cce8d3ee38e00cd259d243a08481e199ee7 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 09:27:52 -0800 Subject: [PATCH 026/121] Renamed the *Attributes assembly to *Extras --- .../Microsoft.Extensions.Logging.Generators.Tests.csproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 14b8e4ecb1ce2..587bbad75df31 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -25,9 +25,8 @@ - - + + From bf365482d18d3e341cbd3eb725cab2534abbd5cc Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 09:51:24 -0800 Subject: [PATCH 027/121] Remove an allocation that snuck in during the last batch of optimizations --- .../gen/LoggingGenerator.cs | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 3060c2852ac78..27eac7742ec54 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -51,6 +51,7 @@ private static string GenType(LoggerClass lc) foreach (var lm in lc.Methods) { methods.Append(GenFormatFunc(lm)); + methods.Append(GenNameArray(lm)); methods.Append(GenLogMethod(lm)); } @@ -132,7 +133,26 @@ private static string GenFormatFunc(LoggerMethod lm) {{ {sb} return $""{lm.Message}""; - }};"; + }}; + "; + } + + private static string GenNameArray(LoggerMethod lm) + { + if (lm.Parameters.Count is < 2 or > MaxStaeHolderArity) + { + return string.Empty; + } + + var sb = new StringBuilder(); + sb.Append($"private static readonly string[] __{lm.Name}Names = new []{{"); + foreach (var p in lm.Parameters) + { + sb.Append($"\"{p.Name}\","); + } + + sb.Append("};"); + return sb.ToString(); } private static string GenLogMethod(LoggerMethod lm) @@ -240,19 +260,7 @@ private static string GenHolder(LoggerMethod lm) var tp = sb.ToString(); sb.Clear(); - sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>(new []{{"); - foreach (var p in lm.Parameters) - { - if (p != lm.Parameters[0]) - { - sb.Append(", "); - } - sb.Append("nameof("); - sb.Append(p.Name); - sb.Append(')'); - } - - sb.Append("}, "); + sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>(__{lm.Name}Names, "); foreach (var p in lm.Parameters) { if (p != lm.Parameters[0]) From b773ce81d3eddaa9d02d45708c6a98eb359668a3 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 10:14:25 -0800 Subject: [PATCH 028/121] Add a few more tests --- .../{LoggingTests.cs => GeneratedTests.cs} | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) rename src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/{LoggingTests.cs => GeneratedTests.cs} (81%) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs similarity index 81% rename from src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs rename to src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs index 8ecb425c2ef6b..0557889d3f312 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggingTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs @@ -24,6 +24,30 @@ partial class LoggerExtensions public static partial void CouldNotOpenSocket(ILogger logger, string hostName); } + // test particular method signature variations are generated correctly + partial class SignatureTests + { + [LoggerMessage(0, LogLevel.Critical, "Message1")] + public static partial void M1(ILogger logger); + + [LoggerMessage(1, LogLevel.Critical, "Message2")] + internal static partial void M2(ILogger logger); + + [LoggerMessage(2, LogLevel.Critical, "Message3")] + private static partial void M3(ILogger logger); + + [LoggerMessage(3, LogLevel.Critical, "Message4")] + private static partial void M4(ILogger logger); + + public static void Combo(ILogger logger, ILogger logger2) + { + M1(logger); + M2(logger); + M3(logger); + M4(logger2); + } + } + partial class ArgTestExtensions { [LoggerMessage(0, LogLevel.Error, "M1")] @@ -54,10 +78,10 @@ partial class ArgTestExtensions public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); } - public class LoggingTests + public class GeneratedTests { [Fact] - public void ExtensionMethodTest() + public void BasicTests() { var logger = new MockLogger(); @@ -152,7 +176,11 @@ public void ReadOnlyListTest() rol = logger.LastState as IReadOnlyList>; Assert.Equal(1, rol!.Count); #pragma warning disable CA1829 // Use Length/Count property instead of Count() when available - Assert.Equal(1, rol.LongCount()); +#pragma warning disable xUnit2013 // Do not use equality check to check for collection size. +#pragma warning disable CA1826 // Do not use Enumerable methods on indexable collections + Assert.Equal(1, rol.Count()); +#pragma warning restore CA1826 // Do not use Enumerable methods on indexable collections +#pragma warning restore xUnit2013 // Do not use equality check to check for collection size. #pragma warning restore CA1829 // Use Length/Count property instead of Count() when available Assert.Equal("p1", (string)rol[0].Key); Assert.Equal("arg1", (string?)rol[0].Value); @@ -163,7 +191,11 @@ public void ReadOnlyListTest() rol = logger.LastState as IReadOnlyList>; Assert.Equal(2, rol!.Count); #pragma warning disable CA1829 // Use Length/Count property instead of Count() when available - Assert.Equal(2, rol.LongCount()); +#pragma warning disable xUnit2013 // Do not use equality check to check for collection size. +#pragma warning disable CA1826 // Do not use Enumerable methods on indexable collections + Assert.Equal(2, rol.Count()); +#pragma warning restore CA1826 // Do not use Enumerable methods on indexable collections +#pragma warning restore xUnit2013 // Do not use equality check to check for collection size. #pragma warning restore CA1829 // Use Length/Count property instead of Count() when available Assert.Equal("p1", (string)rol[0].Key); Assert.Equal("arg1", (string?)rol[0].Value); From 6b25016bdf0622c3c6350c5754ebb31076decceb Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 10:41:55 -0800 Subject: [PATCH 029/121] Use proper code to get fully-qualified type names --- .../gen/LoggingGenerator.Parser.cs | 11 +++-------- .../GeneratedTests.cs | 9 +++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index a5a52a576e369..2bdd1cb0ae0c1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -293,15 +293,10 @@ public IEnumerable GetLogClasses(List class foreach (var p in method.ParameterList.Parameters) { var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; - - // BUGBUG: Terrible hack, need a real solution - var nspace = pSymbol.ContainingNamespace.ToString(); - var typeName = p.Type!.ToString(); -#pragma warning disable CA1308 // Normalize strings to uppercase - if (!string.IsNullOrWhiteSpace(pSymbol.ContainingNamespace.ToString()) && typeName.ToLowerInvariant() != typeName) -#pragma warning restore CA1308 // Normalize strings to uppercase + var typeName = pSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + if (pSymbol.NullableAnnotation == NullableAnnotation.Annotated) { - typeName = nspace + "." + typeName; + typeName += '?'; } if (first) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs index 0557889d3f312..ab5c4af21cab9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs @@ -27,24 +27,33 @@ partial class LoggerExtensions // test particular method signature variations are generated correctly partial class SignatureTests { + // normal public method [LoggerMessage(0, LogLevel.Critical, "Message1")] public static partial void M1(ILogger logger); + // internal method [LoggerMessage(1, LogLevel.Critical, "Message2")] internal static partial void M2(ILogger logger); + // private method [LoggerMessage(2, LogLevel.Critical, "Message3")] private static partial void M3(ILogger logger); + // generic ILogger [LoggerMessage(3, LogLevel.Critical, "Message4")] private static partial void M4(ILogger logger); + // random type method parameter + [LoggerMessage(4, LogLevel.Critical, "Message5")] + private static partial void M5(ILogger logger, System.Collections.IEnumerable items); + public static void Combo(ILogger logger, ILogger logger2) { M1(logger); M2(logger); M3(logger); M4(logger2); + M5(logger, new string[] { "A" }); } } From 1eb4e9fb9db161658cb6b0be42a44a944010324d Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 10:50:19 -0800 Subject: [PATCH 030/121] Add support for message strings containing linefeeds or quotes --- .../Microsoft.Extensions.Logging/gen/LoggingGenerator.cs | 9 +++++++-- .../GeneratedTests.cs | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 27eac7742ec54..bcdae02039f6b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -132,7 +132,7 @@ private static string GenFormatFunc(LoggerMethod lm) return $@"private static readonly global::System.Func<{typeName}, global::System.Exception?, string> __{lm.Name}FormatFunc = (__holder, _) => {{ {sb} - return $""{lm.Message}""; + return $""{EscapeMessageString(lm.Message)}""; }}; "; } @@ -169,7 +169,7 @@ private static string GenLogMethod(LoggerMethod lm) var loggerArg = $"{lm.LoggerType} __logger"; - var formatCall = $"(_, _) => \"{ lm.Message}\""; + var formatCall = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; if (lm.MessageHasTemplates) { if (lm.Parameters.Count == 0) @@ -206,6 +206,11 @@ private static string GenLogMethod(LoggerMethod lm) "; } + private static string EscapeMessageString(string message) + { + return message.Replace("\n", "\\n").Replace("\"", "\\\""); + } + private static string GenParameters(LoggerMethod lm) { var sb = new StringBuilder(); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs index ab5c4af21cab9..9f7d4b92f49f7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs @@ -47,6 +47,12 @@ partial class SignatureTests [LoggerMessage(4, LogLevel.Critical, "Message5")] private static partial void M5(ILogger logger, System.Collections.IEnumerable items); + // linefeeds and quotes in the message string + [LoggerMessage(5, LogLevel.Critical, "Message6\n\"")] + private static partial void M6(ILogger logger); + + + public static void Combo(ILogger logger, ILogger logger2) { M1(logger); @@ -54,6 +60,7 @@ public static void Combo(ILogger logger, ILogger logger2) M3(logger); M4(logger2); M5(logger, new string[] { "A" }); + M6(logger); } } From bb453365cfc9333526b97e0aec8832dd87b28c5c Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 10:52:25 -0800 Subject: [PATCH 031/121] Add support for logging messages containing carriage returns. --- .../Microsoft.Extensions.Logging/gen/LoggingGenerator.cs | 5 ++++- .../GeneratedTests.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index bcdae02039f6b..09f8bfd54acfc 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -208,7 +208,10 @@ private static string GenLogMethod(LoggerMethod lm) private static string EscapeMessageString(string message) { - return message.Replace("\n", "\\n").Replace("\"", "\\\""); + return message + .Replace("\n", "\\n") + .Replace("\r", "\\r") + .Replace("\"", "\\\""); } private static string GenParameters(LoggerMethod lm) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs index 9f7d4b92f49f7..8dd1e0f70e37b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs @@ -48,7 +48,7 @@ partial class SignatureTests private static partial void M5(ILogger logger, System.Collections.IEnumerable items); // linefeeds and quotes in the message string - [LoggerMessage(5, LogLevel.Critical, "Message6\n\"")] + [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] private static partial void M6(ILogger logger); From e979d5fdeb4dedf12fe0211eb449beb69bc698f7 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 11:00:46 -0800 Subject: [PATCH 032/121] Use static analysis consistently --- .../GeneratedTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs index 8dd1e0f70e37b..c43948057899b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs @@ -51,8 +51,6 @@ partial class SignatureTests [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] private static partial void M6(ILogger logger); - - public static void Combo(ILogger logger, ILogger logger2) { M1(logger); From c459d32d0be87c0cedd7d60529ef29fd5a344406 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 11:36:09 -0800 Subject: [PATCH 033/121] More tests, and a fix for string formatting of large cardinality log messages --- .../gen/LoggingGenerator.cs | 2 +- ...eneratedTests.cs => GeneratedCodeTests.cs} | 123 +++++++++++++----- 2 files changed, 90 insertions(+), 35 deletions(-) rename src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/{GeneratedTests.cs => GeneratedCodeTests.cs} (68%) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 09f8bfd54acfc..91f50abd8748a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -100,7 +100,7 @@ private static string GenFormatFunc(LoggerMethod lm) { sb.Append("var "); sb.Append(p.Name); - sb.AppendFormat(CultureInfo.InvariantCulture, " = __holder[{0}];\n", index++); + sb.AppendFormat(CultureInfo.InvariantCulture, " = __holder[{0}].Value;\n", index++); } } else diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs similarity index 68% rename from src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs rename to src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index c43948057899b..08d477bd7ffc0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -92,7 +92,34 @@ partial class ArgTestExtensions public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); } - public class GeneratedTests + partial class ReadOnlyListExtensions + { + [LoggerMessage(0, LogLevel.Error, "M0")] + public static partial void M0(ILogger logger); + + [LoggerMessage(1, LogLevel.Error, "M1")] + public static partial void M1(ILogger logger, int p0); + + [LoggerMessage(2, LogLevel.Error, "M2")] + public static partial void M2(ILogger logger, int p0, int p1); + + [LoggerMessage(3, LogLevel.Error, "M3")] + public static partial void M3(ILogger logger, int p0, int p1, int p2); + + [LoggerMessage(4, LogLevel.Error, "M4")] + public static partial void M4(ILogger logger, int p0, int p1, int p2, int p3); + + [LoggerMessage(5, LogLevel.Error, "M5")] + public static partial void M5(ILogger logger, int p0, int p1, int p2, int p3, int p4); + + [LoggerMessage(6, LogLevel.Error, "M6")] + public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); + + [LoggerMessage(7, LogLevel.Error, "M7")] + public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); + } + + public class GeneratedCodeTests { [Fact] public void BasicTests() @@ -171,6 +198,16 @@ public void ArgTest() Assert.Equal("B", logger.LastException!.Message); Assert.Equal("M7", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method8(logger, 1, 2, 3, 4, 5, 6, 7); + Assert.Equal("M8", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method9(logger, 1, 2, 3, 4, 5, 6, 7); + Assert.Equal("M9 1 2 3 4 5 6 7", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); } [Fact] @@ -179,43 +216,61 @@ public void ReadOnlyListTest() var logger = new MockLogger(); logger.Reset(); - ArgTestExtensions.Method1(logger); - var rol = logger.LastState as IReadOnlyList>; - Assert.Equal(0, rol!.Count); - Assert.Empty(rol); - Assert.Throws(() => _ = rol[0]); + ReadOnlyListExtensions.M0(logger); + TestCollection(0, logger); logger.Reset(); - ArgTestExtensions.Method2(logger, "arg1"); - rol = logger.LastState as IReadOnlyList>; - Assert.Equal(1, rol!.Count); -#pragma warning disable CA1829 // Use Length/Count property instead of Count() when available -#pragma warning disable xUnit2013 // Do not use equality check to check for collection size. -#pragma warning disable CA1826 // Do not use Enumerable methods on indexable collections - Assert.Equal(1, rol.Count()); -#pragma warning restore CA1826 // Do not use Enumerable methods on indexable collections -#pragma warning restore xUnit2013 // Do not use equality check to check for collection size. -#pragma warning restore CA1829 // Use Length/Count property instead of Count() when available - Assert.Equal("p1", (string)rol[0].Key); - Assert.Equal("arg1", (string?)rol[0].Value); - Assert.Throws(() => _ = rol[1]); + ReadOnlyListExtensions.M1(logger, 0); + TestCollection(1, logger); logger.Reset(); - ArgTestExtensions.Method3(logger, "arg1", 2); - rol = logger.LastState as IReadOnlyList>; - Assert.Equal(2, rol!.Count); -#pragma warning disable CA1829 // Use Length/Count property instead of Count() when available -#pragma warning disable xUnit2013 // Do not use equality check to check for collection size. -#pragma warning disable CA1826 // Do not use Enumerable methods on indexable collections - Assert.Equal(2, rol.Count()); -#pragma warning restore CA1826 // Do not use Enumerable methods on indexable collections -#pragma warning restore xUnit2013 // Do not use equality check to check for collection size. -#pragma warning restore CA1829 // Use Length/Count property instead of Count() when available - Assert.Equal("p1", (string)rol[0].Key); - Assert.Equal("arg1", (string?)rol[0].Value); - Assert.Equal("p2", (string)rol[1].Key); - Assert.Equal(2, (int?)rol[1].Value); - Assert.Throws(() => _ = rol[2]); + ReadOnlyListExtensions.M2(logger, 0, 1); + TestCollection(2, logger); + + logger.Reset(); + ReadOnlyListExtensions.M3(logger, 0, 1, 2); + TestCollection(3, logger); + + logger.Reset(); + ReadOnlyListExtensions.M4(logger, 0, 1, 2, 3); + TestCollection(4, logger); + + logger.Reset(); + ReadOnlyListExtensions.M5(logger, 0, 1, 2, 3, 4); + TestCollection(5, logger); + + logger.Reset(); + ReadOnlyListExtensions.M6(logger, 0, 1, 2, 3, 4, 5); + TestCollection(6, logger); + + logger.Reset(); + ReadOnlyListExtensions.M7(logger, 0, 1, 2, 3, 4, 5, 6); + TestCollection(7, logger); + } + + private static void TestCollection(int expected, MockLogger logger) + { + var rol = (logger.LastState as IReadOnlyList>)!; + Assert.NotNull(rol); + + Assert.Equal(expected, rol.Count); + for (int i = 0; i < expected; i++) + { + var kvp = new KeyValuePair($"p{i}", i); + Assert.Equal(kvp, rol[i]); + } + + int count = 0; + foreach (var actual in rol) + { + var kvp = new KeyValuePair($"p{count}", count); + Assert.Equal(kvp, actual); + count++; + } + + Assert.Equal(expected, count); + + Assert.Throws(() => _ = rol[expected]); } } } From 14530de419a7c84309602038af15d10c2e7845dc Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 15:18:36 -0800 Subject: [PATCH 034/121] Add code coverage for logging generator error paths --- .../gen/LoggingGenerator.Parser.cs | 60 ++-- .../gen/LoggingGenerator.cs | 4 - .../GeneratedCodeTests.cs | 1 - .../GeneratorErrorTests.cs | 264 ++++++++++++++++++ ...Extensions.Logging.Generators.Tests.csproj | 7 +- .../Test.cs | 29 ++ 6 files changed, 335 insertions(+), 30 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Test.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 2bdd1cb0ae0c1..82b9d56cb1e8b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -1,21 +1,23 @@ // © Microsoft Corporation. All rights reserved. [assembly: System.Resources.NeutralResourcesLanguage("en-us")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.Extensions.Logging.Generators.Tests")] namespace Microsoft.Extensions.Logging.Generators { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using System.Reflection; + using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; - public partial class LoggingGenerator { - private class Parser + internal class Parser { private const string DiagnosticCategory = "LoggingGenerator"; @@ -117,49 +119,58 @@ private class Parser DiagnosticSeverity.Error, isEnabledByDefault: true); + private readonly CancellationToken _cancellationToken; private readonly Compilation _compilation; - private readonly GeneratorExecutionContext _context; + private readonly Action _reportDiagnostic; private readonly Dictionary _semanticModels = new(); - public Parser(GeneratorExecutionContext context) + public Parser(GeneratorExecutionContext context) : this(context.Compilation, context.ReportDiagnostic, context.CancellationToken) { - _context = context; - _compilation = context.Compilation; + } + + public Parser(Compilation compilation, Action reportDiagnostic, CancellationToken cancellationToken) + { + _compilation = compilation; + _cancellationToken = cancellationToken; + _reportDiagnostic = reportDiagnostic; } /// - /// Gets the set of logging classes contains methods to output. + /// Gets the set of logging classes containing methods to output. /// - public IEnumerable GetLogClasses(List classes) + public IEnumerable GetLogClasses(IEnumerable classes) { + var results = new List(); + var loggerMessageAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); if (loggerMessageAttribute is null) { Diag(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute"); - yield break; + return results; } var exSymbol = _compilation.GetTypeByMetadataName("System.Exception"); if (exSymbol == null) { Diag(ErrorMissingRequiredType, null, "System.Exception"); - yield break; + return results; } var voidSymbol = _compilation.GetTypeByMetadataName("System.Void"); if (voidSymbol == null) { Diag(ErrorMissingRequiredType, null, "System.Void"); - yield break; + return results; } var loggerSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILogger"); if (loggerSymbol == null) { Diag(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.ILogger"); - yield break; + return results; } + var ids = new HashSet(); foreach (var classDef in classes) { // determine the namespace the class is declared in, if any @@ -181,7 +192,7 @@ public IEnumerable GetLogClasses(List class Name = classDef.Identifier.ToString(), }; - var ids = new HashSet(); + ids.Clear(); foreach (var method in classDef.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) { foreach (var mal in method.AttributeLists) @@ -189,7 +200,7 @@ public IEnumerable GetLogClasses(List class foreach (var ma in mal.Attributes) { var semanticModel = GetSemanticModel(ma.SyntaxTree); - var maSymbol = semanticModel.GetSymbolInfo(ma, _context.CancellationToken); + var maSymbol = semanticModel.GetSymbolInfo(ma, _cancellationToken); if (maSymbol.Symbol is IMethodSymbol ms && loggerMessageAttribute.Equals(ms.ContainingType, SymbolEqualityComparer.Default)) { var arg = ma.ArgumentList!.Arguments[0]; @@ -293,11 +304,7 @@ public IEnumerable GetLogClasses(List class foreach (var p in method.ParameterList.Parameters) { var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; - var typeName = pSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - if (pSymbol.NullableAnnotation == NullableAnnotation.Annotated) - { - typeName += '?'; - } + var typeName = pSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier)); if (first) { @@ -346,13 +353,18 @@ public IEnumerable GetLogClasses(List class } } - yield return lc; + if (lc.Methods.Count > 0) + { + results.Add(lc); + } } + + return results; } private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs) { - _context.ReportDiagnostic(Diagnostic.Create(desc, location, messageArgs)); + _reportDiagnostic(Diagnostic.Create(desc, location, messageArgs)); } // Workaround for https://github.com/dotnet/roslyn/pull/49330 @@ -411,7 +423,7 @@ private static bool HasTemplates(string message) #pragma warning disable SA1401 // Fields should be private // An logging class holding a bunch of log methods - private class LoggerClass + internal class LoggerClass { public string? Namespace; public string Name = string.Empty; @@ -419,7 +431,7 @@ private class LoggerClass } // A log method in a logging class - private class LoggerMethod + internal class LoggerMethod { public string Name = string.Empty; public string Message = string.Empty; @@ -433,7 +445,7 @@ private class LoggerMethod } // A single parameter to a log method - private class LoggerParameter + internal class LoggerParameter { public string Name = string.Empty; public string Type = string.Empty; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 91f50abd8748a..bf662140ca497 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -2,13 +2,9 @@ namespace Microsoft.Extensions.Logging.Generators { - using System; using System.Collections.Generic; - using System.Data; using System.Globalization; - using System.Net.Mime; using System.Text; - using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index 08d477bd7ffc0..92f526c3885db 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.Extensions.Logging; using Xunit; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs new file mode 100644 index 0000000000000..47159f372f825 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs @@ -0,0 +1,264 @@ +// © Microsoft Corporation. All rights reserved. + +namespace Microsoft.Extensions.Logging.Generators.Tests +{ + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Threading; + using Xunit; + + public class GeneratorTests + { + [Fact] + public void InvalidMethodName() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial void __M1(ILogger logger); + }} + "); + + Assert.Single(d); + Assert.Equal("LG0", d[0].Id); + } + + [Fact] + public void InvalidMessage() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, """")] + public static partial void M1(ILogger logger); + }} + "); + + Assert.Single(d); + Assert.Equal("LG1", d[0].Id); + } + + [Fact] + public void InvalidParameterName() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial void M1(ILogger logger. string __foo); + }} + "); + + Assert.Single(d); + Assert.Equal("LG2", d[0].Id); + } + + [Fact] + public void NestedType() + { + var d = TryCode(@" + partial class C + {{ + public class Nested + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial void M1(ILogger logger); + }} + }} + "); + + Assert.Single(d); + Assert.Equal("LG3", d[0].Id); + } + + [Fact] + public void RequiredType() + { + var d = TryCode(@" + partial class C + {{ + }} + ", false); + + Assert.Single(d); + Assert.Equal("LG4", d[0].Id); + } + + [Fact] + public void EventIdReuse() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial void M1(ILogger logger); + + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial void M2(ILogger logger); + }} + "); + + Assert.Single(d); + Assert.Equal("LG5", d[0].Id); + } + + [Fact] + public void MethodReturnType() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial int M1(ILogger logger); + }} + "); + + Assert.Single(d); + Assert.Equal("LG6", d[0].Id); + } + + [Fact] + public void FirstArgILogger() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial void M1(int p1, ILogger logger); + }} + "); + + Assert.Single(d); + Assert.Equal("LG7", d[0].Id); + } + + [Fact] + public void NotStatic() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public partial void M1(ILogger logger); + }} + "); + + Assert.Single(d); + Assert.Equal("LG8", d[0].Id); + } + + [Fact] + public void NotPartial() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static void M1(ILogger logger) {} + }} + "); + + Assert.Single(d); + Assert.Equal("LG9", d[0].Id); + } + + [Fact] + public void MethodGeneric() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial void M1(ILogger logger); + }} + "); + + Assert.Single(d); + Assert.Equal("LG10", d[0].Id); + } + + [Fact] + public void ParameterGeneric() + { + var d = TryCode(@" + partial class C + {{ + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial void M1(ILogger logger, T foo) {} + }} + "); + + Assert.Single(d); + Assert.Equal("LG11", d[0].Id); + } + + private static IReadOnlyList TryCode(string code, bool wrap = true) + { + var results = new List(); + + var text = code; + if (wrap) + { + text = $@" + namespace Microsoft.Extensions.Logging + {{ + public enum LogLevel + {{ + Debug, + Information, + Warning, + Error, + }} + + [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] + public sealed class LoggerMessageAttribute : System.Attribute + {{ + public LoggerMessageAttribute(int eventId, LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); + public int EventId {{ get; set; }} + public string? EventName {{ get; set; }} + public LogLevel Level {{ get; set; }} + public string Message {{ get; set; }} + }} + + public interface ILogger + {{ + }} + }} + + namespace Test + {{ + using Microsoft.Extensions.Logging; + + {code} + }} + "; + } + + var compilation = CSharpCompilation.Create( + "example.dll", + new[] + { + CSharpSyntaxTree.ParseText(text), + }, + new[] + { + MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location), + }); + + var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(compilation, (d) => { + results.Add(d); + }, CancellationToken.None); + + var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); + var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); + _ = p.GetLogClasses(allClasses); + + return results; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 587bbad75df31..e84be51716329 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -8,6 +8,11 @@ + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -26,7 +31,7 @@ - + diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Test.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Test.cs new file mode 100644 index 0000000000000..68d397406c0c4 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Test.cs @@ -0,0 +1,29 @@ +namespace Microsoft.Extensions.Logging +{ + public enum LogLevel + { + Debug, + Information, + Warning, + Error, + } + + [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] + public sealed class LoggerMessageAttribute : System.Attribute + { + public LoggerMessageAttribute(int eventId, LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); + public int EventId { get; set; } + public string? EventName { get; set; } + public LogLevel Level { get; set; } + public string Message { get; set; } + } + + public interface ILogger + { + } +} + +namespace Test +{ + using Microsoft.Extensions.Logging; +} \ No newline at end of file From d0ebfb2cb11974dd94c501ad504092b1116ab985 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 15:31:10 -0800 Subject: [PATCH 035/121] Readme update --- .../GeneratorErrorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs index 47159f372f825..403799c69ec0a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests using System.Threading; using Xunit; - public class GeneratorTests + public class GeneratorErrorTests { [Fact] public void InvalidMethodName() From 6354e8fab092a433b641e5581f6fc6e5ce64db15 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 17:44:31 -0800 Subject: [PATCH 036/121] Add support for early termination via the cancellation token --- .../gen/LoggingGenerator.Parser.cs | 12 ++++++++++++ .../gen/LoggingGenerator.cs | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 82b9d56cb1e8b..a7afe6e29c2e7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -173,6 +173,12 @@ public IEnumerable GetLogClasses(IEnumerable(); foreach (var classDef in classes) { + if (_cancellationToken.IsCancellationRequested) + { + // be nice and stop if we're asked to + return results; + } + // determine the namespace the class is declared in, if any NamespaceDeclarationSyntax? ns = null; if (classDef.Parent != null) @@ -195,6 +201,12 @@ public IEnumerable GetLogClasses(IEnumerable m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) { + if (_cancellationToken.IsCancellationRequested) + { + // be nice and stop if we're asked to + return results; + } + foreach (var mal in method.AttributeLists) { foreach (var ma in mal.Attributes) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index bf662140ca497..3bf7c13949d6e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -35,6 +35,12 @@ public void Execute(GeneratorExecutionContext context) var types = new StringBuilder(); foreach (var lc in p.GetLogClasses(receiver.ClassDeclarations)) { + if (context.CancellationToken.IsCancellationRequested) + { + // stop any additional work + return; + } + types.Append(GenType(lc)); } From 8ece9201890def9e3c2a5b1fd6ea2c6936fdd1f1 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 18:07:55 -0800 Subject: [PATCH 037/121] Improvements in the code generator's performance --- .../gen/LoggingGenerator.cs | 295 +++++++++++------- 1 file changed, 178 insertions(+), 117 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 3bf7c13949d6e..14e4a13affcac 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -3,7 +3,6 @@ namespace Microsoft.Extensions.Logging.Generators { using System.Collections.Generic; - using System.Globalization; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -13,8 +12,12 @@ namespace Microsoft.Extensions.Logging.Generators [Generator] public partial class LoggingGenerator : ISourceGenerator { + // The maximum arity of the LogStateHolder-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation + // for the array and boxing of all logging method arguments. const int MaxStaeHolderArity = 6; + private readonly Stack _builders = new(); + /// public void Initialize(GeneratorInitializationContext context) { @@ -32,132 +35,155 @@ public void Execute(GeneratorExecutionContext context) var p = new Parser(context); - var types = new StringBuilder(); - foreach (var lc in p.GetLogClasses(receiver.ClassDeclarations)) + var sb = GetStringBuilder(); + try { - if (context.CancellationToken.IsCancellationRequested) + foreach (var lc in p.GetLogClasses(receiver.ClassDeclarations)) { - // stop any additional work - return; + if (context.CancellationToken.IsCancellationRequested) + { + // stop any additional work + return; + } + + sb.Append(GenType(lc)); } - types.Append(GenType(lc)); + context.AddSource(nameof(LoggingGenerator), SourceText.From(sb.ToString(), Encoding.UTF8)); + } + finally + { + ReturnStringBuilder(sb); } - - context.AddSource(nameof(LoggingGenerator), SourceText.From(types.ToString(), Encoding.UTF8)); } - private static string GenType(LoggerClass lc) + private string GenType(LoggerClass lc) { - var methods = new StringBuilder(); - foreach (var lm in lc.Methods) + var sb = GetStringBuilder(); + try { - methods.Append(GenFormatFunc(lm)); - methods.Append(GenNameArray(lm)); - methods.Append(GenLogMethod(lm)); - } + foreach (var lm in lc.Methods) + { + sb.Append(GenFormatFunc(lm)); + sb.Append(GenNameArray(lm)); + sb.Append(GenLogMethod(lm)); + } - if (string.IsNullOrWhiteSpace(lc.Namespace)) - { - return $@" + if (string.IsNullOrWhiteSpace(lc.Namespace)) + { + return $@" partial class {lc.Name} {{ - {methods} + {sb} }} "; - } + } - return $@" + return $@" namespace {lc.Namespace} {{ partial class {lc.Name} {{ - {methods} + {sb} }} }} "; + } + finally + { + ReturnStringBuilder(sb); + } } - private static string GenFormatFunc(LoggerMethod lm) + private string GenFormatFunc(LoggerMethod lm) { - string typeName; - var sb = new StringBuilder(); - if (lm.Parameters.Count == 0 || !lm.MessageHasTemplates) { return string.Empty; } - else if (lm.Parameters.Count == 1) - { - typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; - sb.Append("var "); - sb.Append(lm.Parameters[0].Name); - sb.Append(" = __holder.Value;\n"); - } - else if (lm.Parameters.Count > MaxStaeHolderArity) + + string typeName; + var sb = GetStringBuilder(); + try { - typeName = $"global::System.Collections.Generic.KeyValuePair[]"; - var index = 0; - foreach (var p in lm.Parameters) + + if (lm.Parameters.Count == 1) { - sb.Append("var "); - sb.Append(p.Name); - sb.AppendFormat(CultureInfo.InvariantCulture, " = __holder[{0}].Value;\n", index++); + typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; + sb.Append($"var {lm.Parameters[0].Name} = __holder.Value;\n"); } - } - else - { - sb.Append("global::Microsoft.Extensions.Logging.LogStateHolder<"); - - foreach (var p in lm.Parameters) + else if (lm.Parameters.Count > MaxStaeHolderArity) { - if (p != lm.Parameters[0]) + typeName = "global::System.Collections.Generic.KeyValuePair[]"; + var index = 0; + foreach (var p in lm.Parameters) { - sb.Append(", "); + sb.Append($"var {p.Name} = __holder[{index++}].Value;\n"); } - - sb.Append(p.Type); } - sb.Append('>'); - typeName = sb.ToString(); - - sb.Clear(); - var index = 1; - foreach (var p in lm.Parameters) + else { - sb.Append("var "); - sb.Append(p.Name); - sb.AppendFormat(CultureInfo.InvariantCulture, " = __holder.Value{0};\n", index++); + sb.Append("global::Microsoft.Extensions.Logging.LogStateHolder<"); + + foreach (var p in lm.Parameters) + { + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } + + sb.Append(p.Type); + } + sb.Append('>'); + typeName = sb.ToString(); + + sb.Clear(); + var index = 1; + foreach (var p in lm.Parameters) + { + sb.Append($"var {p.Name} = __holder.Value{index++};\n"); + } } - } - return $@"private static readonly global::System.Func<{typeName}, global::System.Exception?, string> __{lm.Name}FormatFunc = (__holder, _) => + return $@"private static readonly global::System.Func<{typeName}, global::System.Exception?, string> __{lm.Name}FormatFunc = (__holder, _) => {{ {sb} return $""{EscapeMessageString(lm.Message)}""; }}; "; + } + finally + { + ReturnStringBuilder(sb); + } } - private static string GenNameArray(LoggerMethod lm) + private string GenNameArray(LoggerMethod lm) { if (lm.Parameters.Count is < 2 or > MaxStaeHolderArity) { return string.Empty; } - var sb = new StringBuilder(); - sb.Append($"private static readonly string[] __{lm.Name}Names = new []{{"); - foreach (var p in lm.Parameters) + var sb = GetStringBuilder(); + try { - sb.Append($"\"{p.Name}\","); - } + sb.Append($"private static readonly string[] __{lm.Name}Names = new []{{"); + foreach (var p in lm.Parameters) + { + sb.Append($"\"{p.Name}\", "); + } - sb.Append("};"); - return sb.ToString(); + sb.Append("};"); + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } } - private static string GenLogMethod(LoggerMethod lm) + private string GenLogMethod(LoggerMethod lm) { string exceptionArg = "null"; foreach (var p in lm.Parameters) @@ -169,9 +195,7 @@ private static string GenLogMethod(LoggerMethod lm) } } - var loggerArg = $"{lm.LoggerType} __logger"; - - var formatCall = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; + string formatCall; if (lm.MessageHasTemplates) { if (lm.Parameters.Count == 0) @@ -183,17 +207,23 @@ private static string GenLogMethod(LoggerMethod lm) formatCall = $"__{lm.Name}FormatFunc"; } } + else + { + formatCall = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; + } - var eventName = $"nameof({lm.Name})"; + string eventIdCall; if (lm.EventName != null) { - eventName = $"\"{lm.EventName}\""; + eventIdCall = $"new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, \"{lm.EventName}\")"; + } + else + { + eventIdCall = $"new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, nameof({lm.Name}))"; } - - var eventIdCall = $"new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName})"; return $@" - {lm.Modifier} static partial void {lm.Name}({loggerArg}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {lm.Modifier} static partial void {lm.Name}({lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (__logger.IsEnabled((global::Microsoft.Extensions.Logging.LogLevel){lm.Level})) {{ @@ -216,74 +246,105 @@ private static string EscapeMessageString(string message) .Replace("\"", "\\\""); } - private static string GenParameters(LoggerMethod lm) + private string GenParameters(LoggerMethod lm) { - var sb = new StringBuilder(); - foreach (var p in lm.Parameters) + var sb = GetStringBuilder(); + try { - if (sb.Length > 0) + foreach (var p in lm.Parameters) { - sb.Append(", "); + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } + + sb.Append($"{p.Type} {p.Name}"); } - sb.Append(p.Type); - sb.Append(' '); - sb.Append(p.Name); + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); } - - return sb.ToString(); } - private static string GenHolder(LoggerMethod lm) + private string GenHolder(LoggerMethod lm) { if (lm.Parameters.Count == 0) { return "new global::Microsoft.Extensions.Logging.LogStateHolder()"; } - else if (lm.Parameters.Count == 1) + + if (lm.Parameters.Count == 1) { return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>(nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name})"; } - else if (lm.Parameters.Count > MaxStaeHolderArity) - { - var sb = new StringBuilder("new []{"); - foreach (var p in lm.Parameters) - { - sb.Append($"new global::System.Collections.Generic.KeyValuePair(\"{p.Name}\", {p.Name}), "); - } - sb.Append('}'); - return sb.ToString(); - } - else + var sb = GetStringBuilder(); + try { - var sb = new StringBuilder(); - foreach (var p in lm.Parameters) + if (lm.Parameters.Count > MaxStaeHolderArity) { - if (sb.Length > 0) + sb.Append("new []{"); + foreach (var p in lm.Parameters) { - sb.Append(", "); + sb.Append($"new global::System.Collections.Generic.KeyValuePair(\"{p.Name}\", {p.Name}), "); } - sb.Append(p.Type); + sb.Append('}'); } - var tp = sb.ToString(); - - sb.Clear(); - sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>(__{lm.Name}Names, "); - foreach (var p in lm.Parameters) + else { - if (p != lm.Parameters[0]) + foreach (var p in lm.Parameters) { - sb.Append(", "); + if (sb.Length > 0) + { + sb.Append(", "); + } + + sb.Append(p.Type); } + var tp = sb.ToString(); - sb.Append(p.Name); - } + sb.Clear(); + sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>(__{lm.Name}Names, "); + foreach (var p in lm.Parameters) + { + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } - sb.Append(')'); + sb.Append(p.Name); + } + + sb.Append(')'); + } return sb.ToString(); } + finally + { + ReturnStringBuilder(sb); + } + } + + // our own cheezy object pool since we can't use the .NET core version + private StringBuilder GetStringBuilder() + { + if (_builders.Count == 0) + { + return new StringBuilder(1024); + } + + var b = _builders.Pop(); + b.Clear(); + return b; + } + + private void ReturnStringBuilder(StringBuilder sb) + { + _builders.Push(sb); } private sealed class SyntaxReceiver : ISyntaxReceiver From 0167f3055b8cafe7f160e91eb03a028a21d7473f Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 20:08:29 -0800 Subject: [PATCH 038/121] Produce an error when [LoggerMessage] is applied to a non-partial method in a non-partial class --- .../gen/LoggingGenerator.Parser.cs | 50 +++++++++---------- .../gen/LoggingGenerator.cs | 5 +- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index a7afe6e29c2e7..ac7f0a31e844f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -179,34 +179,10 @@ public IEnumerable GetLogClasses(IEnumerable m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) { - if (_cancellationToken.IsCancellationRequested) - { - // be nice and stop if we're asked to - return results; - } - foreach (var mal in method.AttributeLists) { foreach (var ma in mal.Attributes) @@ -358,6 +334,28 @@ public IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable 0) + if (lc != null) { results.Add(lc); } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 14e4a13affcac..5b899472f16ee 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -353,8 +353,9 @@ private sealed class SyntaxReceiver : ISyntaxReceiver public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { - // Any partial class - if (syntaxNode is ClassDeclarationSyntax { Modifiers: { Count: > 0 } modifiers } classSyntax && modifiers.Any(SyntaxKind.PartialKeyword)) + // Any class + var classSyntax = syntaxNode as ClassDeclarationSyntax; + if (classSyntax != null) { ClassDeclarations.Add(classSyntax); } From 9915bbd35971c52e1673087b6e5285c6f7d269c4 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 21:02:28 -0800 Subject: [PATCH 039/121] Add support for generic parameters for logging methods --- .../gen/LoggingGenerator.Parser.cs | 21 +++++++------------ .../gen/LoggingGenerator.cs | 21 ++++++++++--------- .../gen/LoggingGeneratorResources.Designer.cs | 18 ---------------- .../gen/LoggingGeneratorResources.resx | 19 ----------------- .../GeneratedCodeTests.cs | 6 +++++- .../GeneratorErrorTests.cs | 15 ------------- 6 files changed, 23 insertions(+), 77 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index ac7f0a31e844f..583552bb027a8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -111,14 +111,6 @@ internal class Parser DiagnosticSeverity.Error, isEnabledByDefault: true); - private static readonly DiagnosticDescriptor ErrorParameterIsGeneric = new( - id: "LG11", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorParameterIsGenericTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorParameterIsGenericMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - private readonly CancellationToken _cancellationToken; private readonly Compilation _compilation; private readonly Action _reportDiagnostic; @@ -324,12 +316,6 @@ public IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable Methods = new(); } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 5b899472f16ee..64896cc5aaf04 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -4,6 +4,7 @@ namespace Microsoft.Extensions.Logging.Generators { using System.Collections.Generic; using System.Text; + using System.Xml; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -72,22 +73,22 @@ private string GenType(LoggerClass lc) if (string.IsNullOrWhiteSpace(lc.Namespace)) { return $@" - partial class {lc.Name} - {{ - {sb} - }} + partial class {lc.Name} {lc.Constraints} + {{ + {sb} + }} "; } return $@" - namespace {lc.Namespace} - {{ - partial class {lc.Name} + namespace { lc.Namespace} {{ - {sb} + partial class {lc.Name} {lc.Constraints} + {{ + {sb} + }} }} - }} - "; + "; } finally { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs index 546c8f63aa67a..944bd410048eb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs @@ -257,23 +257,5 @@ internal static string ErrorNotStaticMethodTitle { return ResourceManager.GetString("ErrorNotStaticMethodTitle", resourceCulture); } } - - /// - /// Looks up a localized string similar to Logging method parameters cannot be generic. - /// - internal static string ErrorParameterIsGenericMessage { - get { - return ResourceManager.GetString("ErrorParameterIsGenericMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging method parameters cannot be generic. - /// - internal static string ErrorParameterIsGenericTitle { - get { - return ResourceManager.GetString("ErrorParameterIsGenericTitle", resourceCulture); - } - } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx index 5bece518a78ab..8689e356390cb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx @@ -117,89 +117,70 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Logging method names cannot start with __ Logging method names cannot start with __ - Missing message for logging method Missing message for logging method {0} - Logging method parameter names cannot start with __ Logging method parameter names cannot start with __ - Logging class cannot be in nested types Logging class cannot be in nested types - Could not find a required type definition Could not find definition for type {0} - Multiple logging messages cannot use the same event id Multiple logging messages are using event id {0} - Logging methods must return void Logging methods must return void - The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - Logging methods must be static Logging methods must be static - Logging methods must be partial Logging methods must be partial - Logging methods cannot be generic Logging methods cannot be generic - - - Logging method parameters cannot be generic - - - Logging method parameters cannot be generic - - \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index 92f526c3885db..95f562ff6d6be 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -24,7 +24,7 @@ partial class LoggerExtensions } // test particular method signature variations are generated correctly - partial class SignatureTests + partial class SignatureTests where T : class { // normal public method [LoggerMessage(0, LogLevel.Critical, "Message1")] @@ -50,6 +50,10 @@ partial class SignatureTests [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] private static partial void M6(ILogger logger); + // generic parameter + [LoggerMessage(6, LogLevel.Critical, "Message7\n\"\r")] + private static partial void M7(ILogger logger, T p1); + public static void Combo(ILogger logger, ILogger logger2) { M1(logger); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs index 403799c69ec0a..357dc027caed7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs @@ -182,21 +182,6 @@ partial class C Assert.Equal("LG10", d[0].Id); } - [Fact] - public void ParameterGeneric() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial void M1(ILogger logger, T foo) {} - }} - "); - - Assert.Single(d); - Assert.Equal("LG11", d[0].Id); - } - private static IReadOnlyList TryCode(string code, bool wrap = true) { var results = new List(); From 43f671c2e9fb1c61e5bed293097e091c1403c013 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 22 Nov 2020 23:25:26 -0800 Subject: [PATCH 040/121] Add support for 'protected internal' and 'protected private' logging methods --- .../gen/LoggingGenerator.Parser.cs | 26 +++++++------------ .../gen/LoggingGenerator.cs | 3 +-- .../GeneratedCodeTests.cs | 10 +++++++ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 583552bb027a8..c23afadae2933 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -148,13 +148,6 @@ public IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable Parameters = new(); public bool MessageHasTemplates; - public string Modifier = string.Empty; + public string Modifiers = string.Empty; public string LoggerType = string.Empty; } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 64896cc5aaf04..b9f23503eba89 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -107,7 +107,6 @@ private string GenFormatFunc(LoggerMethod lm) var sb = GetStringBuilder(); try { - if (lm.Parameters.Count == 1) { typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; @@ -224,7 +223,7 @@ private string GenLogMethod(LoggerMethod lm) } return $@" - {lm.Modifier} static partial void {lm.Name}({lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {lm.Modifiers} static partial void {lm.Name}({lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (__logger.IsEnabled((global::Microsoft.Extensions.Logging.LogLevel){lm.Level})) {{ diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index 95f562ff6d6be..9467228353f94 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -54,6 +54,14 @@ partial class SignatureTests where T : class [LoggerMessage(6, LogLevel.Critical, "Message7\n\"\r")] private static partial void M7(ILogger logger, T p1); + // normal public method + [LoggerMessage(7, LogLevel.Critical, "Message8")] + private protected static partial void M8(ILogger logger); + + // internal method + [LoggerMessage(8, LogLevel.Critical, "Message9")] + internal protected static partial void M9(ILogger logger); + public static void Combo(ILogger logger, ILogger logger2) { M1(logger); @@ -62,6 +70,8 @@ public static void Combo(ILogger logger, ILogger logger2) M4(logger2); M5(logger, new string[] { "A" }); M6(logger); + M8(logger); + M9(logger); } } From 66310a154ac72e3c70fbb75116a72e26a8ffd483 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 23 Nov 2020 08:03:36 -0800 Subject: [PATCH 041/121] Minor cleanup --- .../gen/LoggingGenerator.Parser.cs | 32 +++++++++---------- .../gen/LoggingGenerator.cs | 16 ++-------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index c23afadae2933..994bad9951f00 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -185,7 +185,8 @@ public IEnumerable GetLogClasses(IEnumerable 3 } args) { @@ -195,13 +196,13 @@ public IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable + /// A logger class holding a bunch of logger methods. + /// internal class LoggerClass { public string? Namespace; @@ -427,21 +423,25 @@ internal class LoggerClass public List Methods = new(); } - // A log method in a logging class + /// + /// A logger method in a logger class. + /// internal class LoggerMethod { public string Name = string.Empty; public string Message = string.Empty; public string Level = string.Empty; public string EventId = string.Empty; - public string? EventName = null!; - public List Parameters = new(); + public string EventName = string.Empty; public bool MessageHasTemplates; public string Modifiers = string.Empty; public string LoggerType = string.Empty; + public List Parameters = new(); } - // A single parameter to a log method + /// + /// A single parameter to a logger method. + /// internal class LoggerParameter { public string Name = string.Empty; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index b9f23503eba89..3db8f6655af46 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -212,24 +212,14 @@ private string GenLogMethod(LoggerMethod lm) formatCall = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; } - string eventIdCall; - if (lm.EventName != null) - { - eventIdCall = $"new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, \"{lm.EventName}\")"; - } - else - { - eventIdCall = $"new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, nameof({lm.Name}))"; - } - return $@" - {lm.Modifiers} static partial void {lm.Name}({lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {lm.Modifiers} void {lm.Name}({lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (__logger.IsEnabled((global::Microsoft.Extensions.Logging.LogLevel){lm.Level})) {{ __logger.Log( (global::Microsoft.Extensions.Logging.LogLevel){lm.Level}, - {eventIdCall}, + new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, ""{lm.EventName}""), {GenHolder(lm)}, {exceptionArg}, {formatCall}); @@ -298,7 +288,7 @@ private string GenHolder(LoggerMethod lm) { foreach (var p in lm.Parameters) { - if (sb.Length > 0) + if (p != lm.Parameters[0]) { sb.Append(", "); } From 58709eb814f98e4dca6b05d71e506ce295532b64 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 23 Nov 2020 08:52:25 -0800 Subject: [PATCH 042/121] Improved code gen for log levels, and add level unit tests --- .../gen/LoggingGenerator.Parser.cs | 6 +- .../gen/LoggingGenerator.cs | 28 +++++- .../GeneratedCodeTests.cs | 89 +++++++++++++++++++ 3 files changed, 117 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 994bad9951f00..578184307bb7f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -180,13 +180,13 @@ public IEnumerable GetLogClasses(IEnumerable 3 } args) { @@ -430,7 +430,7 @@ internal class LoggerMethod { public string Name = string.Empty; public string Message = string.Empty; - public string Level = string.Empty; + public int Level; public string EventId = string.Empty; public string EventName = string.Empty; public bool MessageHasTemplates; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 3db8f6655af46..748edbab6b8bf 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -185,6 +185,28 @@ private string GenNameArray(LoggerMethod lm) private string GenLogMethod(LoggerMethod lm) { + string level = lm.Level switch + { + 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", + 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", + 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", + 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", + 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", + 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", + 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", + _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", + }; + + string eventName; + if (string.IsNullOrWhiteSpace(lm.EventName)) + { + eventName = $"nameof({lm.Name})"; + } + else + { + eventName = $"\"{lm.EventName}\""; + } + string exceptionArg = "null"; foreach (var p in lm.Parameters) { @@ -215,11 +237,11 @@ private string GenLogMethod(LoggerMethod lm) return $@" {lm.Modifiers} void {lm.Name}({lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ - if (__logger.IsEnabled((global::Microsoft.Extensions.Logging.LogLevel){lm.Level})) + if (__logger.IsEnabled({level})) {{ __logger.Log( - (global::Microsoft.Extensions.Logging.LogLevel){lm.Level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, ""{lm.EventName}""), + {level}, + new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), {GenHolder(lm)}, {exceptionArg}, {formatCall}); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index 9467228353f94..c616831624c65 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -132,6 +132,33 @@ partial class ReadOnlyListExtensions public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); } + partial class LevelTestExtensions + { + [LoggerMessage(0, LogLevel.Trace, "M0")] + public static partial void M0(ILogger logger); + + [LoggerMessage(1, LogLevel.Debug, "M1")] + public static partial void M1(ILogger logger); + + [LoggerMessage(2, LogLevel.Information, "M2")] + public static partial void M2(ILogger logger); + + [LoggerMessage(3, LogLevel.Warning, "M3")] + public static partial void M3(ILogger logger); + + [LoggerMessage(4, LogLevel.Error, "M4")] + public static partial void M4(ILogger logger); + + [LoggerMessage(5, LogLevel.Critical, "M5")] + public static partial void M5(ILogger logger); + + [LoggerMessage(6, LogLevel.None, "M6")] + public static partial void M6(ILogger logger); + + [LoggerMessage(7, LogLevel.None, "M7")] + public static partial void M7(ILogger logger); + } + public class GeneratedCodeTests { [Fact] @@ -285,5 +312,67 @@ private static void TestCollection(int expected, MockLogger logger) Assert.Throws(() => _ = rol[expected]); } + + [Fact] + public void LevelTests() + { + var logger = new MockLogger(); + + logger.Reset(); + LevelTestExtensions.M0(logger); + Assert.Null(logger.LastException); + Assert.Equal("M0", logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M1(logger); + Assert.Null(logger.LastException); + Assert.Equal("M1", logger.LastFormattedString); + Assert.Equal(LogLevel.Debug, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M2(logger); + Assert.Null(logger.LastException); + Assert.Equal("M2", logger.LastFormattedString); + Assert.Equal(LogLevel.Information, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M3(logger); + Assert.Null(logger.LastException); + Assert.Equal("M3", logger.LastFormattedString); + Assert.Equal(LogLevel.Warning, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M4(logger); + Assert.Null(logger.LastException); + Assert.Equal("M4", logger.LastFormattedString); + Assert.Equal(LogLevel.Error, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M5(logger); + Assert.Null(logger.LastException); + Assert.Equal("M5", logger.LastFormattedString); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M6(logger); + Assert.Null(logger.LastException); + Assert.Equal("M6", logger.LastFormattedString); + Assert.Equal(LogLevel.None, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M7(logger); + Assert.Null(logger.LastException); + Assert.Equal("M7", logger.LastFormattedString); + Assert.Equal(LogLevel.None, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + } } } From 11b5ef1672d4777ff4f1bc3fb23d1b341b32b4f0 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 23 Nov 2020 12:34:07 -0800 Subject: [PATCH 043/121] Add support for ToString in the log state --- .../gen/LoggingGenerator.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 748edbab6b8bf..409e4a173c87b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -114,7 +114,8 @@ private string GenFormatFunc(LoggerMethod lm) } else if (lm.Parameters.Count > MaxStaeHolderArity) { - typeName = "global::System.Collections.Generic.KeyValuePair[]"; + typeName = "global::Microsoft.Extensions.Logging.LogStateHolderN"; + var index = 0; foreach (var p in lm.Parameters) { @@ -217,21 +218,21 @@ private string GenLogMethod(LoggerMethod lm) } } - string formatCall; + string formatFunc; if (lm.MessageHasTemplates) { if (lm.Parameters.Count == 0) { - formatCall = "Microsoft.Extensions.Logging.LogStateHolder.Format"; + formatFunc = "Microsoft.Extensions.Logging.LogStateHolder.Format"; } else { - formatCall = $"__{lm.Name}FormatFunc"; + formatFunc = $"__{lm.Name}FormatFunc"; } } else { - formatCall = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; + formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; } return $@" @@ -242,9 +243,9 @@ private string GenLogMethod(LoggerMethod lm) __logger.Log( {level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - {GenHolder(lm)}, + {GenHolder(lm, formatFunc)}, {exceptionArg}, - {formatCall}); + {formatFunc}); }} }} "; @@ -281,7 +282,7 @@ private string GenParameters(LoggerMethod lm) } } - private string GenHolder(LoggerMethod lm) + private string GenHolder(LoggerMethod lm, string formatFunc) { if (lm.Parameters.Count == 0) { @@ -290,7 +291,7 @@ private string GenHolder(LoggerMethod lm) if (lm.Parameters.Count == 1) { - return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>(nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name})"; + return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>({formatFunc}, nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name})"; } var sb = GetStringBuilder(); @@ -298,13 +299,13 @@ private string GenHolder(LoggerMethod lm) { if (lm.Parameters.Count > MaxStaeHolderArity) { - sb.Append("new []{"); + sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolderN({formatFunc}, new global::System.Collections.Generic.KeyValuePair[]{{"); foreach (var p in lm.Parameters) { - sb.Append($"new global::System.Collections.Generic.KeyValuePair(\"{p.Name}\", {p.Name}), "); + sb.Append($"new(\"{p.Name}\", {p.Name}), "); } - sb.Append('}'); + sb.Append("})"); } else { @@ -320,7 +321,7 @@ private string GenHolder(LoggerMethod lm) var tp = sb.ToString(); sb.Clear(); - sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>(__{lm.Name}Names, "); + sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>({formatFunc}, __{lm.Name}Names, "); foreach (var p in lm.Parameters) { if (p != lm.Parameters[0]) From da8099b71c4a47c83250aeae2d3d044876199538 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 25 Nov 2020 07:07:31 -0800 Subject: [PATCH 044/121] Introduce an analyzer. - The analyzer flags use of legacy logging methods. The matching fixer hasn't been implemented yet, but it'll make it easy to convert a legacy logging method call into a new strongly-typed logger call instead. - Add support for convert logging method parameters to pascal case. --- .../gen/LoggingGenerator.Parser.cs | 66 +++++++++---------- .../gen/LoggingGenerator.cs | 49 ++++++++++---- ...osoft.Extensions.Logging.Generators.csproj | 8 +-- ...rces.Designer.cs => Resources.Designer.cs} | 6 +- ...GeneratorResources.resx => Resources.resx} | 0 5 files changed, 78 insertions(+), 51 deletions(-) rename src/libraries/Microsoft.Extensions.Logging/gen/{LoggingGeneratorResources.Designer.cs => Resources.Designer.cs} (98%) rename src/libraries/Microsoft.Extensions.Logging/gen/{LoggingGeneratorResources.resx => Resources.resx} (100%) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 578184307bb7f..190da40bcfaa8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -24,89 +24,89 @@ internal class Parser #pragma warning disable RS2008 // Enable analyzer release tracking private static readonly DiagnosticDescriptor ErrorInvalidMethodName = new( - id: "LG0", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMethodNameTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMethodNameMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0000", + title: new LocalizableResourceString(nameof(Resources.ErrorInvalidMethodNameTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorInvalidMethodNameMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidMessage = new( - id: "LG1", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMessageTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMessageMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0001", + title: new LocalizableResourceString(nameof(Resources.ErrorInvalidMessageTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorInvalidMessageMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidParameterName = new( - id: "LG2", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidParameterNameTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidParameterNameMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0002", + title: new LocalizableResourceString(nameof(Resources.ErrorInvalidParameterNameTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorInvalidParameterNameMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNestedType = new( - id: "LG3", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNestedTypeTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNestedTypeMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0003", + title: new LocalizableResourceString(nameof(Resources.ErrorNestedTypeTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorNestedTypeMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorMissingRequiredType = new( - id: "LG4", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorMissingRequiredTypeTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorMissingRequiredTypeMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0004", + title: new LocalizableResourceString(nameof(Resources.ErrorMissingRequiredTypeTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorMissingRequiredTypeMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorEventIdReuse = new( - id: "LG5", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorEventIdReuseTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorEventIdReuseMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0005", + title: new LocalizableResourceString(nameof(Resources.ErrorEventIdReuseTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorEventIdReuseMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidMethodReturnType = new( - id: "LG6", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMethodReturnTypeTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorInvalidMethodReturnTypeMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0006", + title: new LocalizableResourceString(nameof(Resources.ErrorInvalidMethodReturnTypeTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorInvalidMethodReturnTypeMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorFirstArgMustBeILogger = new( - id: "LG7", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorFirstArgMustBeILoggerTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorFirstArgMustBeILoggerMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0007", + title: new LocalizableResourceString(nameof(Resources.ErrorFirstArgMustBeILoggerTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorFirstArgMustBeILoggerMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNotStaticMethod = new( - id: "LG8", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNotStaticMethodTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNotStaticMethodMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0008", + title: new LocalizableResourceString(nameof(Resources.ErrorNotStaticMethodTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorNotStaticMethodMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNotPartialMethod = new( - id: "LG9", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNotPartialMethodTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorNotPartialMethodMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0009", + title: new LocalizableResourceString(nameof(Resources.ErrorNotPartialMethodTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorNotPartialMethodMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorMethodIsGeneric = new( - id: "LG10", - title: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorMethodIsGenericTitle), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), - messageFormat: new LocalizableResourceString(nameof(LoggingGeneratorResources.ErrorMethodIsGenericMessage), LoggingGeneratorResources.ResourceManager, typeof(LoggingGeneratorResources)), + id: "LG0010", + title: new LocalizableResourceString(nameof(Resources.ErrorMethodIsGenericTitle), Resources.ResourceManager, typeof(Resources)), + messageFormat: new LocalizableResourceString(nameof(Resources.ErrorMethodIsGenericMessage), Resources.ResourceManager, typeof(Resources)), category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 409e4a173c87b..03a0ac439eb07 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -18,6 +18,7 @@ public partial class LoggingGenerator : ISourceGenerator const int MaxStaeHolderArity = 6; private readonly Stack _builders = new(); + private bool _pascalCaseArguments; /// public void Initialize(GeneratorInitializationContext context) @@ -34,6 +35,12 @@ public void Execute(GeneratorExecutionContext context) return; } + _pascalCaseArguments = false; + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("PascalCaseArguments", out var value)) + { + _pascalCaseArguments = ((value.ToUpperInvariant() == "TRUE") || (value.ToUpperInvariant() == "YES")); + } + var p = new Parser(context); var sb = GetStringBuilder(); @@ -172,7 +179,7 @@ private string GenNameArray(LoggerMethod lm) sb.Append($"private static readonly string[] __{lm.Name}Names = new []{{"); foreach (var p in lm.Parameters) { - sb.Append($"\"{p.Name}\", "); + sb.Append($"\"{NormalizeArgumentName(p.Name)}\", "); } sb.Append("};"); @@ -251,14 +258,6 @@ private string GenLogMethod(LoggerMethod lm) "; } - private static string EscapeMessageString(string message) - { - return message - .Replace("\n", "\\n") - .Replace("\r", "\\r") - .Replace("\"", "\\\""); - } - private string GenParameters(LoggerMethod lm) { var sb = GetStringBuilder(); @@ -291,7 +290,7 @@ private string GenHolder(LoggerMethod lm, string formatFunc) if (lm.Parameters.Count == 1) { - return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>({formatFunc}, nameof({lm.Parameters[0].Name}), {lm.Parameters[0].Name})"; + return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>({formatFunc}, \"{NormalizeArgumentName(lm.Parameters[0].Name)}\", {lm.Parameters[0].Name})"; } var sb = GetStringBuilder(); @@ -302,7 +301,7 @@ private string GenHolder(LoggerMethod lm, string formatFunc) sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolderN({formatFunc}, new global::System.Collections.Generic.KeyValuePair[]{{"); foreach (var p in lm.Parameters) { - sb.Append($"new(\"{p.Name}\", {p.Name}), "); + sb.Append($"new(\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); } sb.Append("})"); @@ -342,6 +341,34 @@ private string GenHolder(LoggerMethod lm, string formatFunc) } } + private string NormalizeArgumentName(string name) + { + if (!_pascalCaseArguments || char.IsUpper(name, 0)) + { + return name; + } + + var sb = GetStringBuilder(); + try + { + sb.Append(char.ToUpperInvariant(name[0])); + sb.Append(name, 1, name.Length - 1); + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private static string EscapeMessageString(string message) + { + return message + .Replace("\n", "\\n") + .Replace("\r", "\\r") + .Replace("\"", "\\\""); + } + // our own cheezy object pool since we can't use the .NET core version private StringBuilder GetStringBuilder() { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 162e16d403033..182d87d647a79 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -16,17 +16,17 @@ - + True True - LoggingGeneratorResources.resx + Resources.resx - + ResXFileCodeGenerator - LoggingGeneratorResources.Designer.cs + Resources.Designer.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs similarity index 98% rename from src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs rename to src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs index 944bd410048eb..8616f759eb7cf 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs @@ -22,14 +22,14 @@ namespace Microsoft.Extensions.Logging.Generators { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class LoggingGeneratorResources { + internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal LoggingGeneratorResources() { + internal Resources() { } /// @@ -39,7 +39,7 @@ internal LoggingGeneratorResources() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Extensions.Logging.Generators.LoggingGeneratorResources", typeof(LoggingGeneratorResources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Extensions.Logging.Generators.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx similarity index 100% rename from src/libraries/Microsoft.Extensions.Logging/gen/LoggingGeneratorResources.resx rename to src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx From dd39eba7d5afb41bd0336da4313291f467bedc55 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 25 Nov 2020 08:47:17 -0800 Subject: [PATCH 045/121] Combine the fixer into the analyzer assembly. --- .../gen/LoggingGenerator.Parser.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 190da40bcfaa8..b350df41db4be 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -25,88 +25,88 @@ internal class Parser private static readonly DiagnosticDescriptor ErrorInvalidMethodName = new( id: "LG0000", - title: new LocalizableResourceString(nameof(Resources.ErrorInvalidMethodNameTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorInvalidMethodNameMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorInvalidMethodNameTitle, + messageFormat: Resources.ErrorInvalidMethodNameMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidMessage = new( id: "LG0001", - title: new LocalizableResourceString(nameof(Resources.ErrorInvalidMessageTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorInvalidMessageMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorInvalidMessageTitle, + messageFormat: Resources.ErrorInvalidMessageMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidParameterName = new( id: "LG0002", - title: new LocalizableResourceString(nameof(Resources.ErrorInvalidParameterNameTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorInvalidParameterNameMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorInvalidParameterNameTitle, + messageFormat: Resources.ErrorInvalidParameterNameMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNestedType = new( id: "LG0003", - title: new LocalizableResourceString(nameof(Resources.ErrorNestedTypeTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorNestedTypeMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorNestedTypeTitle, + messageFormat: Resources.ErrorNestedTypeMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorMissingRequiredType = new( id: "LG0004", - title: new LocalizableResourceString(nameof(Resources.ErrorMissingRequiredTypeTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorMissingRequiredTypeMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorMissingRequiredTypeTitle, + messageFormat: Resources.ErrorMissingRequiredTypeMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorEventIdReuse = new( id: "LG0005", - title: new LocalizableResourceString(nameof(Resources.ErrorEventIdReuseTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorEventIdReuseMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorEventIdReuseTitle, + messageFormat: Resources.ErrorEventIdReuseMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorInvalidMethodReturnType = new( id: "LG0006", - title: new LocalizableResourceString(nameof(Resources.ErrorInvalidMethodReturnTypeTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorInvalidMethodReturnTypeMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorInvalidMethodReturnTypeTitle, + messageFormat: Resources.ErrorInvalidMethodReturnTypeMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorFirstArgMustBeILogger = new( id: "LG0007", - title: new LocalizableResourceString(nameof(Resources.ErrorFirstArgMustBeILoggerTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorFirstArgMustBeILoggerMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorFirstArgMustBeILoggerTitle, + messageFormat: Resources.ErrorFirstArgMustBeILoggerMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNotStaticMethod = new( id: "LG0008", - title: new LocalizableResourceString(nameof(Resources.ErrorNotStaticMethodTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorNotStaticMethodMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorNotStaticMethodTitle, + messageFormat: Resources.ErrorNotStaticMethodMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorNotPartialMethod = new( id: "LG0009", - title: new LocalizableResourceString(nameof(Resources.ErrorNotPartialMethodTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorNotPartialMethodMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorNotPartialMethodTitle, + messageFormat: Resources.ErrorNotPartialMethodMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); private static readonly DiagnosticDescriptor ErrorMethodIsGeneric = new( id: "LG0010", - title: new LocalizableResourceString(nameof(Resources.ErrorMethodIsGenericTitle), Resources.ResourceManager, typeof(Resources)), - messageFormat: new LocalizableResourceString(nameof(Resources.ErrorMethodIsGenericMessage), Resources.ResourceManager, typeof(Resources)), + title: Resources.ErrorMethodIsGenericTitle, + messageFormat: Resources.ErrorMethodIsGenericMessage, category: DiagnosticCategory, DiagnosticSeverity.Error, isEnabledByDefault: true); From a93b5c0f5ba5306ad701414bf716d62a2b6c6e67 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Fri, 27 Nov 2020 11:22:52 -0800 Subject: [PATCH 046/121] Introduce analyzer and fixer functionality --- .../gen/LoggingGenerator.Parser.cs | 3 +++ .../gen/LoggingGenerator.cs | 2 +- .../GeneratorErrorTests.cs | 22 +++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index b350df41db4be..1ecf3dd5b3814 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -202,6 +202,7 @@ public IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable Parameters = new(); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 03a0ac439eb07..42c0d8a896de3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -243,7 +243,7 @@ private string GenLogMethod(LoggerMethod lm) } return $@" - {lm.Modifiers} void {lm.Name}({lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if (__logger.IsEnabled({level})) {{ diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs index 357dc027caed7..40bdbe6cbcdd5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs @@ -25,7 +25,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0", d[0].Id); + Assert.Equal("LG0000", d[0].Id); } [Fact] @@ -40,7 +40,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG1", d[0].Id); + Assert.Equal("LG0001", d[0].Id); } [Fact] @@ -55,7 +55,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG2", d[0].Id); + Assert.Equal("LG0002", d[0].Id); } [Fact] @@ -73,7 +73,7 @@ public class Nested "); Assert.Single(d); - Assert.Equal("LG3", d[0].Id); + Assert.Equal("LG0003", d[0].Id); } [Fact] @@ -86,7 +86,7 @@ partial class C ", false); Assert.Single(d); - Assert.Equal("LG4", d[0].Id); + Assert.Equal("LG0004", d[0].Id); } [Fact] @@ -104,7 +104,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG5", d[0].Id); + Assert.Equal("LG0005", d[0].Id); } [Fact] @@ -119,7 +119,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG6", d[0].Id); + Assert.Equal("LG0006", d[0].Id); } [Fact] @@ -134,7 +134,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG7", d[0].Id); + Assert.Equal("LG0007", d[0].Id); } [Fact] @@ -149,7 +149,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG8", d[0].Id); + Assert.Equal("LG0008", d[0].Id); } [Fact] @@ -164,7 +164,7 @@ public static void M1(ILogger logger) {} "); Assert.Single(d); - Assert.Equal("LG9", d[0].Id); + Assert.Equal("LG0009", d[0].Id); } [Fact] @@ -179,7 +179,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG10", d[0].Id); + Assert.Equal("LG0010", d[0].Id); } private static IReadOnlyList TryCode(string code, bool wrap = true) From 17d3a5bae69bb50e3ae6c30a542768acbc382ea8 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Fri, 27 Nov 2020 17:36:39 -0800 Subject: [PATCH 047/121] More stuff. - Build a testing infra to make it possible to test the fixer. - Updating dependencies across projects - The analyzer now highlights Log() methods and the fixer can deal with those too. --- ...Extensions.Logging.Generators.Tests.csproj | 6 +--- .../Test.cs | 29 ------------------- 2 files changed, 1 insertion(+), 34 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Test.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index e84be51716329..120728f10e27d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -7,17 +7,13 @@ true - - - - all runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Test.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Test.cs deleted file mode 100644 index 68d397406c0c4..0000000000000 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Test.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Microsoft.Extensions.Logging -{ - public enum LogLevel - { - Debug, - Information, - Warning, - Error, - } - - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] - public sealed class LoggerMessageAttribute : System.Attribute - { - public LoggerMessageAttribute(int eventId, LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); - public int EventId { get; set; } - public string? EventName { get; set; } - public LogLevel Level { get; set; } - public string Message { get; set; } - } - - public interface ILogger - { - } -} - -namespace Test -{ - using Microsoft.Extensions.Logging; -} \ No newline at end of file From 561dbf3164640e2074dfb370433bb9832baeae90 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 28 Nov 2020 12:30:19 -0800 Subject: [PATCH 048/121] More tests, and ensuing bug fixes. --- .../gen/LoggingGenerator.Parser.cs | 125 +++-- .../gen/LoggingGenerator.cs | 10 +- .../GeneratedCodeTests.cs | 40 +- .../GeneratorErrorTests.cs | 249 ---------- .../GeneratorParserTests.cs | 467 ++++++++++++++++++ 5 files changed, 563 insertions(+), 328 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 1ecf3dd5b3814..89a1721bf00d0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -5,15 +5,14 @@ namespace Microsoft.Extensions.Logging.Generators { + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; - using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; public partial class LoggingGenerator { @@ -116,10 +115,6 @@ internal class Parser private readonly Action _reportDiagnostic; private readonly Dictionary _semanticModels = new(); - public Parser(GeneratorExecutionContext context) : this(context.Compilation, context.ReportDiagnostic, context.CancellationToken) - { - } - public Parser(Compilation compilation, Action reportDiagnostic, CancellationToken cancellationToken) { _compilation = compilation; @@ -132,19 +127,19 @@ public Parser(Compilation compilation, Action reportDiagnostic, Canc /// public IEnumerable GetLogClasses(IEnumerable classes) { - var results = new List(); + var results = new List(); - var loggerMessageAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); - if (loggerMessageAttribute is null) + var exSymbol = _compilation.GetTypeByMetadataName("System.Exception"); + if (exSymbol == null) { - Diag(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute"); + Diag(ErrorMissingRequiredType, null, "System.Exception"); return results; } - var exSymbol = _compilation.GetTypeByMetadataName("System.Exception"); - if (exSymbol == null) + var loggerMessageAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); + if (loggerMessageAttribute is null) { - Diag(ErrorMissingRequiredType, null, "System.Exception"); + Diag(ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute"); return results; } @@ -165,6 +160,8 @@ public IEnumerable GetLogClasses(IEnumerable m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) { @@ -172,31 +169,31 @@ public IEnumerable GetLogClasses(IEnumerable 3 } args) + if (ma.ArgumentList.Arguments.Count > 3) { - arg = args[3]; - eventName = semanticModel.GetConstantValue(arg.Expression).ToString(); + arg = ma.ArgumentList.Arguments[3]; + eventName = sm.GetConstantValue(arg.Expression).ToString(); } var lm = new LoggerMethod { - Name = methodName, + Name = method.Identifier.ToString(), Level = level, Message = message, EventId = eventId, @@ -259,6 +256,8 @@ public IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable private static bool HasTemplates(string message) { - for (int i = 0; i < message.Length; i++) + int start = message.IndexOf('{'); + if (start < 0) { - var ch = message[i]; - if (ch == '{') - { - if (i < message.Length - 1 && message[i + 1] != '{') - { - // look for a non-escaped } - i++; - for (; i < message.Length; i++) - { - ch = message[i]; - if (ch == '}') - { - if (i == message.Length - 1 || message[i + 1] != '}') - { - return true; - } - } - } - - return false; - } - } + return false; } - return false; + return message.IndexOf('}', start) > 0; } } @@ -419,7 +414,7 @@ private static bool HasTemplates(string message) /// internal class LoggerClass { - public string? Namespace; + public string Namespace = string.Empty; public string Name = string.Empty; public string Constraints = string.Empty; public List Methods = new(); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index 42c0d8a896de3..fdb7f78d6904d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -2,13 +2,11 @@ namespace Microsoft.Extensions.Logging.Generators { - using System.Collections.Generic; - using System.Text; - using System.Xml; using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; + using System.Collections.Generic; + using System.Text; [Generator] public partial class LoggingGenerator : ISourceGenerator @@ -41,7 +39,7 @@ public void Execute(GeneratorExecutionContext context) _pascalCaseArguments = ((value.ToUpperInvariant() == "TRUE") || (value.ToUpperInvariant() == "YES")); } - var p = new Parser(context); + var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); var sb = GetStringBuilder(); try @@ -88,7 +86,7 @@ partial class {lc.Name} {lc.Constraints} } return $@" - namespace { lc.Namespace} + namespace {lc.Namespace} {{ partial class {lc.Name} {lc.Constraints} {{ diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index c616831624c65..01c4375f9bc63 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -8,21 +8,37 @@ #pragma warning disable CA1801 // Review unused parameters // Used to test use outside of a namespace -partial class LoggerExtensionsNoNamespace +partial class NoNamespace { [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - public static partial void CouldNotOpenSocketNoNamespace(ILogger logger, string hostName); + public static partial void CouldNotOpenSocket(ILogger logger, string hostName); } -namespace Microsoft.Extensions.Logging.Generators.Tests +namespace Level1 { - // used to test use inside a namespace - partial class LoggerExtensions + // used to test use inside a one-level namespace + partial class OneLevelNamespace { [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] public static partial void CouldNotOpenSocket(ILogger logger, string hostName); } +} + +namespace Level1 +{ + namespace Level2 + { + // used to test use inside a two-level namespace + partial class TwoLevelNamespace + { + [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + public static partial void CouldNotOpenSocket(ILogger logger, string hostName); + } + } +} +namespace Microsoft.Extensions.Logging.Generators.Tests +{ // test particular method signature variations are generated correctly partial class SignatureTests where T : class { @@ -167,18 +183,26 @@ public void BasicTests() var logger = new MockLogger(); logger.Reset(); - LoggerExtensions.CouldNotOpenSocket(logger, "microsoft.com"); + NoNamespace.CouldNotOpenSocket(logger, "microsoft.com"); Assert.Equal(LogLevel.Critical, logger.LastLogLevel); Assert.Null(logger.LastException); Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); - LoggerExtensionsNoNamespace.CouldNotOpenSocketNoNamespace(logger, "microsoft.com"); + Level1.OneLevelNamespace.CouldNotOpenSocket(logger, "microsoft.com"); Assert.Equal(LogLevel.Critical, logger.LastLogLevel); Assert.Null(logger.LastException); Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); + + logger.Reset(); + Level1.Level2.TwoLevelNamespace.CouldNotOpenSocket(logger, "microsoft.com"); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Null(logger.LastException); + Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); + } [Fact] @@ -188,7 +212,7 @@ public void EnableTest() logger.Reset(); logger.Enabled = false; - LoggerExtensions.CouldNotOpenSocket(logger, "microsoft.com"); + NoNamespace.CouldNotOpenSocket(logger, "microsoft.com"); Assert.Equal(0, logger.CallCount); // ensure the logger doesn't get called when it is disabled } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs deleted file mode 100644 index 40bdbe6cbcdd5..0000000000000 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorErrorTests.cs +++ /dev/null @@ -1,249 +0,0 @@ -// © Microsoft Corporation. All rights reserved. - -namespace Microsoft.Extensions.Logging.Generators.Tests -{ - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Threading; - using Xunit; - - public class GeneratorErrorTests - { - [Fact] - public void InvalidMethodName() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial void __M1(ILogger logger); - }} - "); - - Assert.Single(d); - Assert.Equal("LG0000", d[0].Id); - } - - [Fact] - public void InvalidMessage() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, """")] - public static partial void M1(ILogger logger); - }} - "); - - Assert.Single(d); - Assert.Equal("LG0001", d[0].Id); - } - - [Fact] - public void InvalidParameterName() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial void M1(ILogger logger. string __foo); - }} - "); - - Assert.Single(d); - Assert.Equal("LG0002", d[0].Id); - } - - [Fact] - public void NestedType() - { - var d = TryCode(@" - partial class C - {{ - public class Nested - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial void M1(ILogger logger); - }} - }} - "); - - Assert.Single(d); - Assert.Equal("LG0003", d[0].Id); - } - - [Fact] - public void RequiredType() - { - var d = TryCode(@" - partial class C - {{ - }} - ", false); - - Assert.Single(d); - Assert.Equal("LG0004", d[0].Id); - } - - [Fact] - public void EventIdReuse() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial void M1(ILogger logger); - - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial void M2(ILogger logger); - }} - "); - - Assert.Single(d); - Assert.Equal("LG0005", d[0].Id); - } - - [Fact] - public void MethodReturnType() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial int M1(ILogger logger); - }} - "); - - Assert.Single(d); - Assert.Equal("LG0006", d[0].Id); - } - - [Fact] - public void FirstArgILogger() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial void M1(int p1, ILogger logger); - }} - "); - - Assert.Single(d); - Assert.Equal("LG0007", d[0].Id); - } - - [Fact] - public void NotStatic() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public partial void M1(ILogger logger); - }} - "); - - Assert.Single(d); - Assert.Equal("LG0008", d[0].Id); - } - - [Fact] - public void NotPartial() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static void M1(ILogger logger) {} - }} - "); - - Assert.Single(d); - Assert.Equal("LG0009", d[0].Id); - } - - [Fact] - public void MethodGeneric() - { - var d = TryCode(@" - partial class C - {{ - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - public static partial void M1(ILogger logger); - }} - "); - - Assert.Single(d); - Assert.Equal("LG0010", d[0].Id); - } - - private static IReadOnlyList TryCode(string code, bool wrap = true) - { - var results = new List(); - - var text = code; - if (wrap) - { - text = $@" - namespace Microsoft.Extensions.Logging - {{ - public enum LogLevel - {{ - Debug, - Information, - Warning, - Error, - }} - - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] - public sealed class LoggerMessageAttribute : System.Attribute - {{ - public LoggerMessageAttribute(int eventId, LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); - public int EventId {{ get; set; }} - public string? EventName {{ get; set; }} - public LogLevel Level {{ get; set; }} - public string Message {{ get; set; }} - }} - - public interface ILogger - {{ - }} - }} - - namespace Test - {{ - using Microsoft.Extensions.Logging; - - {code} - }} - "; - } - - var compilation = CSharpCompilation.Create( - "example.dll", - new[] - { - CSharpSyntaxTree.ParseText(text), - }, - new[] - { - MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location), - }); - - var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(compilation, (d) => { - results.Add(d); - }, CancellationToken.None); - - var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); - var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); - _ = p.GetLogClasses(allClasses); - - return results; - } - } -} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs new file mode 100644 index 0000000000000..68e9e201dce4c --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs @@ -0,0 +1,467 @@ +// © Microsoft Corporation. All rights reserved. + +namespace Microsoft.Extensions.Logging.Generators.Tests +{ + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Threading; + using Xunit; + + public class GeneratorParserTests + { + [Fact] + public void InvalidMethodName() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void __M1(ILogger logger); + } + "); + + Assert.Single(lc); + Assert.Single(d); + Assert.Equal("LG0000", d[0].Id); + } + + [Fact] + public void InvalidMessage() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, """")] + static partial void M1(ILogger logger); + } + "); + + Assert.Single(lc); + Assert.Single(d); + Assert.Equal("LG0001", d[0].Id); + } + + [Fact] + public void InvalidParameterName() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger, string __foo); + } + "); + + Assert.Single(lc); + Assert.Single(d); + Assert.Equal("LG0002", d[0].Id); + } + + [Fact] + public void NestedType() + { + var (lc, d) = TryCode(@" + partial class C + { + public partial class Nested + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + } + } + "); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0003", d[0].Id); + } + + [Fact] + public void RequiredTypes() + { + var (lc, d) = TryCode(@" + namespace System + { + public class Object + { + } + + public class Void + { + } + } + namespace Microsoft.Extensions.Logging + { + } + partial class C + { + } + ", false, includeReferences: false); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0004", d[0].Id); + + (lc, d) = TryCode(@" + partial class C + { + } + ", false); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0004", d[0].Id); + + (lc, d) = TryCode(@" + namespace Microsoft.Extensions.Logging + { + public sealed class LoggerMessageAttribute : System.Attribute {} + } + partial class C + { + } + ", false); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0004", d[0].Id); + } + + [Fact] + public void EventIdReuse() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M2(ILogger logger); + } + "); + + Assert.Single(lc); + Assert.Single(d); + Assert.Equal("LG0005", d[0].Id); + } + + [Fact] + public void MethodReturnType() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public static partial int M1(ILogger logger); + + public static partial int M1(ILogger logger) { return 0; } + } + "); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0006", d[0].Id); + } + + [Fact] + public void FirstArgILogger() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(int p1, ILogger logger); + } + "); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0007", d[0].Id); + } + + [Fact] + public void NotStatic() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + partial void M1(ILogger logger); + } + "); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0008", d[0].Id); + } + + [Fact] + public void NotPartial() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static void M1(ILogger logger) {} + } + "); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0009", d[0].Id); + } + + [Fact] + public void MethodGeneric() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Empty(lc); + Assert.Single(d); + Assert.Equal("LG0010", d[0].Id); + } + + [Fact] + public void Templates() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(1, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger, string arg1, string arg2); + + [LoggerMessage(2, LogLevel.Debug, ""M2 {arg1} {arg2}"")] + static partial void M2(ILogger logger, string arg1, string arg2); + + [LoggerMessage(3, LogLevel.Debug, ""M3 {arg1"")] + static partial void M3(ILogger logger, string arg1); + + [LoggerMessage(4, LogLevel.Debug, ""M4 arg1}"")] + static partial void M4(ILogger logger, string arg1); + + [LoggerMessage(5, LogLevel.Debug, ""M5 {"")] + static partial void M5(ILogger logger, string arg1); + + [LoggerMessage(6, LogLevel.Debug, ""}M6 "")] + static partial void M6(ILogger logger, string arg1); + + [LoggerMessage(7, LogLevel.Debug, ""M7 {{arg1}}"")] + static partial void M7(ILogger logger, string arg1); + } + "); + + Assert.Single(lc); + Assert.False(lc.First().Methods[0].MessageHasTemplates); + Assert.True(lc.First().Methods[1].MessageHasTemplates); + Assert.False(lc.First().Methods[2].MessageHasTemplates); + Assert.False(lc.First().Methods[3].MessageHasTemplates); + Assert.False(lc.First().Methods[4].MessageHasTemplates); + Assert.False(lc.First().Methods[5].MessageHasTemplates); + Assert.True(lc.First().Methods[6].MessageHasTemplates); + Assert.Empty(d); + } + + [Fact] + public void Namespace() + { + var (lc, d) = TryCode(@" + namespace Foo + { + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + } + } + "); + + Assert.Single(lc); + Assert.Equal("Test.Foo", lc.First().Namespace); + Assert.Equal("C", lc.First().Name); + Assert.Empty(d); + + (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Single(lc); + Assert.Equal("Test", lc.First().Namespace); + Assert.Equal("C", lc.First().Name); + Assert.Empty(d); + + (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + } + ", true, false); + + Assert.Single(lc); + Assert.Equal(string.Empty, lc.First().Namespace); + Assert.Equal("C", lc.First().Name); + Assert.Empty(d); + } + + [Fact] + public void Generic() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Single(lc); + Assert.Equal("Test", lc.First().Namespace); + Assert.Equal("C", lc.First().Name); + Assert.Empty(d); + } + + [Fact] + public void EventName() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"", EventName = ""MyEvent"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Single(lc); + Assert.Equal("Test", lc.First().Namespace); + Assert.Equal("C", lc.First().Name); + Assert.Equal("MyEvent", lc.First().Methods[0].EventName); + Assert.Empty(d); + } + + [Fact] + public void Cancellation() + { + var (lc, d) = TryCode(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + } + ", cancellationToken: new CancellationToken(true)); + + Assert.Empty(lc); + Assert.Empty(d); + } + + [Fact] + public void RandomAttribute() + { + var (lc, d) = TryCode(@" + partial class C + { + [System.Obsolete(""Foo"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Empty(lc); + Assert.Empty(d); + } + + private static (IEnumerable, IReadOnlyList) TryCode( + string code, + bool wrap = true, + bool inNamespace = true, + bool includeReferences = true, + CancellationToken cancellationToken = default) + { + var text = code; + if (wrap) + { + var nsStart = "namespace Test {"; + var nsEnd = "}"; + if (!inNamespace) + { + nsStart = ""; + nsEnd = ""; + } + + text = $@" + {nsStart} + using Microsoft.Extensions.Logging; + {code} + {nsEnd} + + namespace Microsoft.Extensions.Logging + {{ + public enum LogLevel + {{ + Trace, + Debug, + Information, + Warning, + Error, + Critical, + }} + + [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] + public sealed class LoggerMessageAttribute : System.Attribute + {{ + public LoggerMessageAttribute(int eventId, LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); + public int EventId {{ get; set; }} + public string? EventName {{ get; set; }} + public LogLevel Level {{ get; set; }} + public string Message {{ get; set; }} + }} + + public interface ILogger + {{ + }} + }} + "; + } + + var refs = Array.Empty(); + if (includeReferences) + { + refs = new[] { MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location) }; + } + + var compilation = CSharpCompilation.Create( + "example.dll", + new[] { CSharpSyntaxTree.ParseText(text, cancellationToken: CancellationToken.None) }, + refs) + .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithNullableContextOptions(NullableContextOptions.Enable)); + + // make sure we have valid syntax + Assert.Empty(compilation.GetDiagnostics(CancellationToken.None)); + + var results = new List(); + var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(compilation, (d) => { + results.Add(d); + }, cancellationToken); + + var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); + var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); + var lc = p.GetLogClasses(allClasses); + + return (lc, results); + } + } +} From 8f94d10b29b8ba8d58328d3d3c7979d77bc763bf Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 28 Nov 2020 22:58:55 -0800 Subject: [PATCH 049/121] Tests and fixes --- .../gen/LoggingGenerator.Parser.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 89a1721bf00d0..9b01bfb1cec57 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -176,19 +176,19 @@ public IEnumerable GetLogClasses(IEnumerable 3) { arg = ma.ArgumentList.Arguments[3]; - eventName = sm.GetConstantValue(arg.Expression).ToString(); + eventName = sm.GetConstantValue(arg.Expression, _cancellationToken).ToString(); } var lm = new LoggerMethod @@ -355,6 +355,8 @@ public IEnumerable GetLogClasses(IEnumerable Date: Mon, 30 Nov 2020 11:02:26 -0800 Subject: [PATCH 050/121] Finish support for nullable logging args, and extension logging methods --- .../gen/LoggingGenerator.Parser.cs | 14 ++--- .../GeneratedCodeTests.cs | 18 +++++++ .../GeneratorParserTests.cs | 53 ++++++++++++------- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 9b01bfb1cec57..3661b1f44886b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -125,7 +125,7 @@ public Parser(Compilation compilation, Action reportDiagnostic, Canc /// /// Gets the set of logging classes containing methods to output. /// - public IEnumerable GetLogClasses(IEnumerable classes) + public IReadOnlyList GetLogClasses(IEnumerable classes) { var results = new List(); @@ -171,9 +171,11 @@ public IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable().First(); + var typeName = GetSemanticModel(p.SyntaxTree).GetDeclaredSymbol(p)!.ToDisplayString(); var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; - var typeName = pSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier)); - // TODO: the above doesn't deal properly with nullable types if (first) { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index 01c4375f9bc63..0a53826e56515 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -78,6 +78,10 @@ partial class SignatureTests where T : class [LoggerMessage(8, LogLevel.Critical, "Message9")] internal protected static partial void M9(ILogger logger); + // nullable parameter + [LoggerMessage(9, LogLevel.Critical, "Message10")] + internal static partial void M10(ILogger logger, string? optional); + public static void Combo(ILogger logger, ILogger logger2) { M1(logger); @@ -88,6 +92,20 @@ public static void Combo(ILogger logger, ILogger logger2) M6(logger); M8(logger); M9(logger); + M10(logger, null); + } + } + + // test particular method signature variations are generated correctly + static partial class SignatureTests + { + // extension method + [LoggerMessage(10, LogLevel.Critical, "Message11")] + internal static partial void M11(this ILogger logger); + + public static void Combo(ILogger logger) + { + logger.M11(); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs index 68e9e201dce4c..1382e437e6af3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs @@ -263,13 +263,13 @@ partial class C "); Assert.Single(lc); - Assert.False(lc.First().Methods[0].MessageHasTemplates); - Assert.True(lc.First().Methods[1].MessageHasTemplates); - Assert.False(lc.First().Methods[2].MessageHasTemplates); - Assert.False(lc.First().Methods[3].MessageHasTemplates); - Assert.False(lc.First().Methods[4].MessageHasTemplates); - Assert.False(lc.First().Methods[5].MessageHasTemplates); - Assert.True(lc.First().Methods[6].MessageHasTemplates); + Assert.False(lc[0].Methods[0].MessageHasTemplates); + Assert.True(lc[0].Methods[1].MessageHasTemplates); + Assert.False(lc[0].Methods[2].MessageHasTemplates); + Assert.False(lc[0].Methods[3].MessageHasTemplates); + Assert.False(lc[0].Methods[4].MessageHasTemplates); + Assert.False(lc[0].Methods[5].MessageHasTemplates); + Assert.True(lc[0].Methods[6].MessageHasTemplates); Assert.Empty(d); } @@ -288,8 +288,8 @@ partial class C "); Assert.Single(lc); - Assert.Equal("Test.Foo", lc.First().Namespace); - Assert.Equal("C", lc.First().Name); + Assert.Equal("Test.Foo", lc[0].Namespace); + Assert.Equal("C", lc[0].Name); Assert.Empty(d); (lc, d) = TryCode(@" @@ -301,8 +301,8 @@ partial class C "); Assert.Single(lc); - Assert.Equal("Test", lc.First().Namespace); - Assert.Equal("C", lc.First().Name); + Assert.Equal("Test", lc[0].Namespace); + Assert.Equal("C", lc[0].Name); Assert.Empty(d); (lc, d) = TryCode(@" @@ -314,8 +314,8 @@ partial class C ", true, false); Assert.Single(lc); - Assert.Equal(string.Empty, lc.First().Namespace); - Assert.Equal("C", lc.First().Name); + Assert.Equal(string.Empty, lc[0].Namespace); + Assert.Equal("C", lc[0].Name); Assert.Empty(d); } @@ -331,8 +331,8 @@ partial class C "); Assert.Single(lc); - Assert.Equal("Test", lc.First().Namespace); - Assert.Equal("C", lc.First().Name); + Assert.Equal("Test", lc[0].Namespace); + Assert.Equal("C", lc[0].Name); Assert.Empty(d); } @@ -348,9 +348,9 @@ partial class C "); Assert.Single(lc); - Assert.Equal("Test", lc.First().Namespace); - Assert.Equal("C", lc.First().Name); - Assert.Equal("MyEvent", lc.First().Methods[0].EventName); + Assert.Equal("Test", lc[0].Namespace); + Assert.Equal("C", lc[0].Name); + Assert.Equal("MyEvent", lc[0].Methods[0].EventName); Assert.Empty(d); } @@ -384,7 +384,22 @@ partial class C Assert.Empty(d); } - private static (IEnumerable, IReadOnlyList) TryCode( + [Fact] + public void ExtensionMethod() + { + var (lc, d) = TryCode(@" + static partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""Hello"")] + static partial void M1(this ILogger logger); + } + "); + + Assert.True(lc[0].Methods[0].IsExtensionMethod); + Assert.Empty(d); + } + + private static (IReadOnlyList, IReadOnlyList) TryCode( string code, bool wrap = true, bool inNamespace = true, From 6fabebc2aa63d838f74043a4314bdfadf1753fdc Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 30 Nov 2020 12:18:29 -0800 Subject: [PATCH 051/121] Add license file and a bit of info in the README --- .../Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 3661b1f44886b..4752c70a4dd68 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -273,7 +273,6 @@ public IReadOnlyList GetLogClasses(IEnumerable().First(); var typeName = GetSemanticModel(p.SyntaxTree).GetDeclaredSymbol(p)!.ToDisplayString(); var pSymbol = GetSemanticModel(p.SyntaxTree).GetTypeInfo(p.Type!).Type!; From 438f2bbd9e9315f4f13bc7c83b3b3f3a2e07e8d7 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 30 Nov 2020 17:37:21 -0800 Subject: [PATCH 052/121] Improve generator's perf to minimize the impact on the IDE --- .../gen/LoggingGenerator.Parser.cs | 99 ++++++++++--------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 4752c70a4dd68..a8f2eaa31b8d9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -1,4 +1,4 @@ -// © Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. [assembly: System.Resources.NeutralResourcesLanguage("en-us")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.Extensions.Logging.Generators.Tests")] @@ -113,7 +113,6 @@ internal class Parser private readonly CancellationToken _cancellationToken; private readonly Compilation _compilation; private readonly Action _reportDiagnostic; - private readonly Dictionary _semanticModels = new(); public Parser(Compilation compilation, Action reportDiagnostic, CancellationToken cancellationToken) { @@ -151,32 +150,50 @@ public IReadOnlyList GetLogClasses(IEnumerable(); - foreach (var classDef in classes) + + // we enumerate by syntax tree, to minimize the need to instantiate semantic models (since they're expensive) + foreach (var group in classes.GroupBy(x => x.SyntaxTree)) { - if (_cancellationToken.IsCancellationRequested) + SemanticModel? sm = null; + foreach (var classDef in group) { - // be nice and stop if we're asked to - return results; - } + if (_cancellationToken.IsCancellationRequested) + { + // be nice and stop if we're asked to + return results; + } - LoggerClass? lc = null; - string nspace = string.Empty; + LoggerClass? lc = null; + string nspace = string.Empty; - ids.Clear(); - foreach (var method in classDef.Members.Where(m => m.IsKind(SyntaxKind.MethodDeclaration)).OfType()) - { - foreach (var mal in method.AttributeLists) + ids.Clear(); + foreach (var member in classDef.Members) { - foreach (var ma in mal.Attributes) + var method = member as MethodDeclarationSyntax; + if (method == null) { - var sm = GetSemanticModel(ma.SyntaxTree); - var maSymbolInfo = sm.GetSymbolInfo(ma, _cancellationToken); - var maSymbol = (maSymbolInfo.Symbol as IMethodSymbol)!; - - var methodSymbol = (sm.GetDeclaredSymbol(method, _cancellationToken) as IMethodSymbol)!; + // we only care about methods + continue; + } - if (loggerMessageAttribute.Equals(maSymbol.ContainingType, SymbolEqualityComparer.Default)) + foreach (var mal in method.AttributeLists) + { + foreach (var ma in mal.Attributes) { + if (sm == null) + { + // need a semantic model for this tree + sm = _compilation.GetSemanticModel(classDef.SyntaxTree); + } + + var maSymbol = (sm.GetSymbolInfo(ma, _cancellationToken).Symbol as IMethodSymbol)!; + + if (!loggerMessageAttribute.Equals(maSymbol.ContainingType, SymbolEqualityComparer.Default)) + { + // not the right attribute, move on + continue; + } + var arg = ma.ArgumentList!.Arguments[0]; var eventId = sm.GetConstantValue(arg.Expression, _cancellationToken).ToString(); @@ -193,6 +210,8 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable 0) { + // don't currently support generic methods Diag(ErrorMethodIsGeneric, method.Identifier.GetLocation()); keep = false; } @@ -258,8 +279,6 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable Date: Mon, 30 Nov 2020 18:30:06 -0800 Subject: [PATCH 053/121] Refactor the main generator class to allow unit testing of the production logic --- .../gen/LoggingGenerator.Emit.cs | 368 ++++++++++++++++++ .../gen/LoggingGenerator.cs | 354 +---------------- 2 files changed, 374 insertions(+), 348 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs new file mode 100644 index 0000000000000..35185df8fba0b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs @@ -0,0 +1,368 @@ +// © Microsoft Corporation. All rights reserved. + +namespace Microsoft.Extensions.Logging.Generators +{ + using System.Collections.Generic; + using System.Text; + using System.Threading; + + public partial class LoggingGenerator + { + internal class Emitter + { + private readonly Stack _builders = new(); + private readonly bool _pascalCaseArguments; + + public Emitter(bool pascalCaseArguments) + { + _pascalCaseArguments = pascalCaseArguments; + } + + public string Emit(IReadOnlyList logClasses, CancellationToken cancellationToken) + { + var sb = GetStringBuilder(); + try + { + foreach (var lc in logClasses) + { + if (cancellationToken.IsCancellationRequested) + { + // stop any additional work + break; + } + + sb.Append(GenType(lc)); + } + + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenType(LoggerClass lc) + { + var sb = GetStringBuilder(); + try + { + foreach (var lm in lc.Methods) + { + sb.Append(GenFormatFunc(lm)); + sb.Append(GenNameArray(lm)); + sb.Append(GenLogMethod(lm)); + } + + if (string.IsNullOrWhiteSpace(lc.Namespace)) + { + return $@" + partial class {lc.Name} {lc.Constraints} + {{ + {sb} + }} + "; + } + + return $@" + namespace {lc.Namespace} + {{ + partial class {lc.Name} {lc.Constraints} + {{ + {sb} + }} + }} + "; + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenFormatFunc(LoggerMethod lm) + { + if (lm.Parameters.Count == 0 || !lm.MessageHasTemplates) + { + return string.Empty; + } + + string typeName; + var sb = GetStringBuilder(); + try + { + if (lm.Parameters.Count == 1) + { + typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; + sb.Append($"var {lm.Parameters[0].Name} = __holder.Value;\n"); + } + else if (lm.Parameters.Count > MaxStateHolderArity) + { + typeName = "global::Microsoft.Extensions.Logging.LogStateHolderN"; + + var index = 0; + foreach (var p in lm.Parameters) + { + sb.Append($"var {p.Name} = __holder[{index++}].Value;\n"); + } + } + else + { + sb.Append("global::Microsoft.Extensions.Logging.LogStateHolder<"); + + foreach (var p in lm.Parameters) + { + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } + + sb.Append(p.Type); + } + sb.Append('>'); + typeName = sb.ToString(); + + sb.Clear(); + var index = 1; + foreach (var p in lm.Parameters) + { + sb.Append($"var {p.Name} = __holder.Value{index++};\n"); + } + } + + return $@"private static readonly global::System.Func<{typeName}, global::System.Exception?, string> __{lm.Name}FormatFunc = (__holder, _) => + {{ + {sb} + return $""{EscapeMessageString(lm.Message)}""; + }}; + "; + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenNameArray(LoggerMethod lm) + { + if (lm.Parameters.Count is < 2 or > MaxStateHolderArity) + { + return string.Empty; + } + + var sb = GetStringBuilder(); + try + { + sb.Append($"private static readonly string[] __{lm.Name}Names = new []{{"); + foreach (var p in lm.Parameters) + { + sb.Append($"\"{NormalizeArgumentName(p.Name)}\", "); + } + + sb.Append("};"); + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenLogMethod(LoggerMethod lm) + { + string level = lm.Level switch + { + 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", + 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", + 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", + 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", + 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", + 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", + 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", + _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", + }; + + string eventName; + if (string.IsNullOrWhiteSpace(lm.EventName)) + { + eventName = $"nameof({lm.Name})"; + } + else + { + eventName = $"\"{lm.EventName}\""; + } + + string exceptionArg = "null"; + foreach (var p in lm.Parameters) + { + if (p.IsExceptionType) + { + exceptionArg = p.Name; + break; + } + } + + string formatFunc; + if (lm.MessageHasTemplates) + { + if (lm.Parameters.Count == 0) + { + formatFunc = "Microsoft.Extensions.Logging.LogStateHolder.Format"; + } + else + { + formatFunc = $"__{lm.Name}FormatFunc"; + } + } + else + { + formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; + } + + return $@" + {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {{ + if (__logger.IsEnabled({level})) + {{ + __logger.Log( + {level}, + new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), + {GenHolder(lm, formatFunc)}, + {exceptionArg}, + {formatFunc}); + }} + }} + "; + } + + private string GenParameters(LoggerMethod lm) + { + var sb = GetStringBuilder(); + try + { + foreach (var p in lm.Parameters) + { + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } + + sb.Append($"{p.Type} {p.Name}"); + } + + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenHolder(LoggerMethod lm, string formatFunc) + { + if (lm.Parameters.Count == 0) + { + return "new global::Microsoft.Extensions.Logging.LogStateHolder()"; + } + + if (lm.Parameters.Count == 1) + { + return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>({formatFunc}, \"{NormalizeArgumentName(lm.Parameters[0].Name)}\", {lm.Parameters[0].Name})"; + } + + var sb = GetStringBuilder(); + try + { + if (lm.Parameters.Count > MaxStateHolderArity) + { + sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolderN({formatFunc}, new global::System.Collections.Generic.KeyValuePair[]{{"); + foreach (var p in lm.Parameters) + { + sb.Append($"new(\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); + } + + sb.Append("})"); + } + else + { + foreach (var p in lm.Parameters) + { + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } + + sb.Append(p.Type); + } + var tp = sb.ToString(); + + sb.Clear(); + sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>({formatFunc}, __{lm.Name}Names, "); + foreach (var p in lm.Parameters) + { + if (p != lm.Parameters[0]) + { + sb.Append(", "); + } + + sb.Append(p.Name); + } + + sb.Append(')'); + } + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string NormalizeArgumentName(string name) + { + if (!_pascalCaseArguments || char.IsUpper(name, 0)) + { + return name; + } + + var sb = GetStringBuilder(); + try + { + sb.Append(char.ToUpperInvariant(name[0])); + sb.Append(name, 1, name.Length - 1); + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private static string EscapeMessageString(string message) + { + return message + .Replace("\n", "\\n") + .Replace("\r", "\\r") + .Replace("\"", "\\\""); + } + + // our own cheezy object pool since we can't use the .NET core version + private StringBuilder GetStringBuilder() + { + if (_builders.Count == 0) + { + return new StringBuilder(1024); + } + + var b = _builders.Pop(); + b.Clear(); + return b; + } + + private void ReturnStringBuilder(StringBuilder sb) + { + _builders.Push(sb); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index fdb7f78d6904d..2389d99c38b8d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -13,10 +13,7 @@ public partial class LoggingGenerator : ISourceGenerator { // The maximum arity of the LogStateHolder-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation // for the array and boxing of all logging method arguments. - const int MaxStaeHolderArity = 6; - - private readonly Stack _builders = new(); - private bool _pascalCaseArguments; + const int MaxStateHolderArity = 6; /// public void Initialize(GeneratorInitializationContext context) @@ -33,356 +30,17 @@ public void Execute(GeneratorExecutionContext context) return; } - _pascalCaseArguments = false; + var pascalCaseArguments = false; if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("PascalCaseArguments", out var value)) { - _pascalCaseArguments = ((value.ToUpperInvariant() == "TRUE") || (value.ToUpperInvariant() == "YES")); + pascalCaseArguments = ((value.ToUpperInvariant() == "TRUE") || (value.ToUpperInvariant() == "YES")); } var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); + var e = new Emitter(pascalCaseArguments); + var result = e.Emit(p.GetLogClasses(receiver.ClassDeclarations), context.CancellationToken); - var sb = GetStringBuilder(); - try - { - foreach (var lc in p.GetLogClasses(receiver.ClassDeclarations)) - { - if (context.CancellationToken.IsCancellationRequested) - { - // stop any additional work - return; - } - - sb.Append(GenType(lc)); - } - - context.AddSource(nameof(LoggingGenerator), SourceText.From(sb.ToString(), Encoding.UTF8)); - } - finally - { - ReturnStringBuilder(sb); - } - } - - private string GenType(LoggerClass lc) - { - var sb = GetStringBuilder(); - try - { - foreach (var lm in lc.Methods) - { - sb.Append(GenFormatFunc(lm)); - sb.Append(GenNameArray(lm)); - sb.Append(GenLogMethod(lm)); - } - - if (string.IsNullOrWhiteSpace(lc.Namespace)) - { - return $@" - partial class {lc.Name} {lc.Constraints} - {{ - {sb} - }} - "; - } - - return $@" - namespace {lc.Namespace} - {{ - partial class {lc.Name} {lc.Constraints} - {{ - {sb} - }} - }} - "; - } - finally - { - ReturnStringBuilder(sb); - } - } - - private string GenFormatFunc(LoggerMethod lm) - { - if (lm.Parameters.Count == 0 || !lm.MessageHasTemplates) - { - return string.Empty; - } - - string typeName; - var sb = GetStringBuilder(); - try - { - if (lm.Parameters.Count == 1) - { - typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; - sb.Append($"var {lm.Parameters[0].Name} = __holder.Value;\n"); - } - else if (lm.Parameters.Count > MaxStaeHolderArity) - { - typeName = "global::Microsoft.Extensions.Logging.LogStateHolderN"; - - var index = 0; - foreach (var p in lm.Parameters) - { - sb.Append($"var {p.Name} = __holder[{index++}].Value;\n"); - } - } - else - { - sb.Append("global::Microsoft.Extensions.Logging.LogStateHolder<"); - - foreach (var p in lm.Parameters) - { - if (p != lm.Parameters[0]) - { - sb.Append(", "); - } - - sb.Append(p.Type); - } - sb.Append('>'); - typeName = sb.ToString(); - - sb.Clear(); - var index = 1; - foreach (var p in lm.Parameters) - { - sb.Append($"var {p.Name} = __holder.Value{index++};\n"); - } - } - - return $@"private static readonly global::System.Func<{typeName}, global::System.Exception?, string> __{lm.Name}FormatFunc = (__holder, _) => - {{ - {sb} - return $""{EscapeMessageString(lm.Message)}""; - }}; - "; - } - finally - { - ReturnStringBuilder(sb); - } - } - - private string GenNameArray(LoggerMethod lm) - { - if (lm.Parameters.Count is < 2 or > MaxStaeHolderArity) - { - return string.Empty; - } - - var sb = GetStringBuilder(); - try - { - sb.Append($"private static readonly string[] __{lm.Name}Names = new []{{"); - foreach (var p in lm.Parameters) - { - sb.Append($"\"{NormalizeArgumentName(p.Name)}\", "); - } - - sb.Append("};"); - return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } - } - - private string GenLogMethod(LoggerMethod lm) - { - string level = lm.Level switch - { - 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", - 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", - 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", - 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", - 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", - 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", - 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", - _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", - }; - - string eventName; - if (string.IsNullOrWhiteSpace(lm.EventName)) - { - eventName = $"nameof({lm.Name})"; - } - else - { - eventName = $"\"{lm.EventName}\""; - } - - string exceptionArg = "null"; - foreach (var p in lm.Parameters) - { - if (p.IsExceptionType) - { - exceptionArg = p.Name; - break; - } - } - - string formatFunc; - if (lm.MessageHasTemplates) - { - if (lm.Parameters.Count == 0) - { - formatFunc = "Microsoft.Extensions.Logging.LogStateHolder.Format"; - } - else - { - formatFunc = $"__{lm.Name}FormatFunc"; - } - } - else - { - formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; - } - - return $@" - {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) - {{ - if (__logger.IsEnabled({level})) - {{ - __logger.Log( - {level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - {GenHolder(lm, formatFunc)}, - {exceptionArg}, - {formatFunc}); - }} - }} - "; - } - - private string GenParameters(LoggerMethod lm) - { - var sb = GetStringBuilder(); - try - { - foreach (var p in lm.Parameters) - { - if (p != lm.Parameters[0]) - { - sb.Append(", "); - } - - sb.Append($"{p.Type} {p.Name}"); - } - - return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } - } - - private string GenHolder(LoggerMethod lm, string formatFunc) - { - if (lm.Parameters.Count == 0) - { - return "new global::Microsoft.Extensions.Logging.LogStateHolder()"; - } - - if (lm.Parameters.Count == 1) - { - return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>({formatFunc}, \"{NormalizeArgumentName(lm.Parameters[0].Name)}\", {lm.Parameters[0].Name})"; - } - - var sb = GetStringBuilder(); - try - { - if (lm.Parameters.Count > MaxStaeHolderArity) - { - sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolderN({formatFunc}, new global::System.Collections.Generic.KeyValuePair[]{{"); - foreach (var p in lm.Parameters) - { - sb.Append($"new(\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); - } - - sb.Append("})"); - } - else - { - foreach (var p in lm.Parameters) - { - if (p != lm.Parameters[0]) - { - sb.Append(", "); - } - - sb.Append(p.Type); - } - var tp = sb.ToString(); - - sb.Clear(); - sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>({formatFunc}, __{lm.Name}Names, "); - foreach (var p in lm.Parameters) - { - if (p != lm.Parameters[0]) - { - sb.Append(", "); - } - - sb.Append(p.Name); - } - - sb.Append(')'); - } - return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } - } - - private string NormalizeArgumentName(string name) - { - if (!_pascalCaseArguments || char.IsUpper(name, 0)) - { - return name; - } - - var sb = GetStringBuilder(); - try - { - sb.Append(char.ToUpperInvariant(name[0])); - sb.Append(name, 1, name.Length - 1); - return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } - } - - private static string EscapeMessageString(string message) - { - return message - .Replace("\n", "\\n") - .Replace("\r", "\\r") - .Replace("\"", "\\\""); - } - - // our own cheezy object pool since we can't use the .NET core version - private StringBuilder GetStringBuilder() - { - if (_builders.Count == 0) - { - return new StringBuilder(1024); - } - - var b = _builders.Pop(); - b.Clear(); - return b; - } - - private void ReturnStringBuilder(StringBuilder sb) - { - _builders.Push(sb); + context.AddSource(nameof(LoggingGenerator), SourceText.From(result, Encoding.UTF8)); } private sealed class SyntaxReceiver : ISyntaxReceiver From 9c6c08bad41ece1e4971eb8f4ec15281930cbae9 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 1 Dec 2020 08:30:40 -0800 Subject: [PATCH 054/121] More test coverage, more resulting fixes --- .../gen/LoggingGenerator.Emit.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs index 35185df8fba0b..24d779e138410 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs @@ -87,10 +87,10 @@ private string GenFormatFunc(LoggerMethod lm) return string.Empty; } - string typeName; var sb = GetStringBuilder(); try { + string typeName; if (lm.Parameters.Count == 1) { typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; @@ -131,10 +131,10 @@ private string GenFormatFunc(LoggerMethod lm) } return $@"private static readonly global::System.Func<{typeName}, global::System.Exception?, string> __{lm.Name}FormatFunc = (__holder, _) => - {{ - {sb} - return $""{EscapeMessageString(lm.Message)}""; - }}; + {{ + {sb} + return $""{EscapeMessageString(lm.Message)}""; + }}; "; } finally @@ -220,19 +220,19 @@ private string GenLogMethod(LoggerMethod lm) } return $@" - {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) - {{ - if (__logger.IsEnabled({level})) + {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ - __logger.Log( - {level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - {GenHolder(lm, formatFunc)}, - {exceptionArg}, - {formatFunc}); + if (__logger.IsEnabled({level})) + {{ + __logger.Log( + {level}, + new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), + {GenHolder(lm, formatFunc)}, + {exceptionArg}, + {formatFunc}); + }} }} - }} - "; + "; } private string GenParameters(LoggerMethod lm) @@ -354,9 +354,9 @@ private StringBuilder GetStringBuilder() return new StringBuilder(1024); } - var b = _builders.Pop(); - b.Clear(); - return b; + var sb = _builders.Pop(); + sb.Clear(); + return sb; } private void ReturnStringBuilder(StringBuilder sb) From 71fd60a52ed779b690e9c0de94b5f75e033b0215 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 1 Dec 2020 18:37:50 -0800 Subject: [PATCH 055/121] Make the source generator robust to malformed source --- .../gen/LoggingGenerator.Emit.cs | 4 ++ .../gen/LoggingGenerator.Parser.cs | 66 ++++++++++++------- .../gen/LoggingGenerator.cs | 11 ++-- .../GeneratorParserTests.cs | 36 +++++++++- 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs index 24d779e138410..fc380212ec9cc 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs @@ -10,6 +10,10 @@ public partial class LoggingGenerator { internal class Emitter { + // The maximum arity of the LogStateHolder-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation + // for the array and boxing of all logging method arguments. + const int MaxStateHolderArity = 6; + private readonly Stack _builders = new(); private readonly bool _pascalCaseArguments; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index a8f2eaa31b8d9..9b50799372183 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -186,31 +186,31 @@ public IReadOnlyList GetLogClasses(IEnumerable 3) + if (args.Count > 3) { - arg = ma.ArgumentList.Arguments[3]; - eventName = sm.GetConstantValue(arg.Expression, _cancellationToken).ToString(); + eventName = sm.GetConstantValue(args[3].Expression, _cancellationToken).ToString(); } - var methodSymbol = (sm.GetDeclaredSymbol(method, _cancellationToken) as IMethodSymbol)!; + var methodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken) as IMethodSymbol; + if (methodSymbol == null) + { + // semantic problem, just bail quietly + continue; + } var lm = new LoggerMethod { @@ -241,7 +241,7 @@ public IReadOnlyList GetLogClasses(IEnumerable 0) { - // don't currently support generic methods + // we don't currently support generic methods Diag(ErrorMethodIsGeneric, method.Identifier.GetLocation()); keep = false; } @@ -277,7 +277,7 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable public void Initialize(GeneratorInitializationContext context) { @@ -24,7 +20,8 @@ public void Initialize(GeneratorInitializationContext context) /// public void Execute(GeneratorExecutionContext context) { - if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) + var receiver = context.SyntaxReceiver as SyntaxReceiver; + if (receiver == null || receiver.ClassDeclarations.Count == 0) { // nothing to do yet return; @@ -38,7 +35,8 @@ public void Execute(GeneratorExecutionContext context) var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); var e = new Emitter(pascalCaseArguments); - var result = e.Emit(p.GetLogClasses(receiver.ClassDeclarations), context.CancellationToken); + var logClasses = p.GetLogClasses(receiver.ClassDeclarations); + var result = e.Emit(logClasses, context.CancellationToken); context.AddSource(nameof(LoggingGenerator), SourceText.From(result, Encoding.UTF8)); } @@ -49,7 +47,6 @@ private sealed class SyntaxReceiver : ISyntaxReceiver public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { - // Any class var classSyntax = syntaxNode as ClassDeclarationSyntax; if (classSyntax != null) { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs index 1382e437e6af3..23607382dcfba 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs @@ -399,11 +399,40 @@ static partial class C Assert.Empty(d); } + [Fact] + public void SourceErrors() + { + var (lc, d) = TryCode(@" + static partial class C + { + // bogus argument type + [LoggerMessage(0, "", ""Hello"")] + static partial void M1(ILogger logger); + + // attribute applied to something other than a method + [LoggerMessage(0, "", ""Hello"")] + int field; + + // missing parameter name + [LoggerMessage(0, LogLevel.Debug, ""Hello"")] + static partial void M2(ILogger); + + // bogus parameter type + [LoggerMessage(0, LogLevel.Debug, ""Hello"")] + static partial void M2(XILogger logger); + } + ", checkDiags: false); + + Assert.Empty(lc); + Assert.Empty(d); // should fail quietly on broken code + } + private static (IReadOnlyList, IReadOnlyList) TryCode( string code, bool wrap = true, bool inNamespace = true, bool includeReferences = true, + bool checkDiags = true, CancellationToken cancellationToken = default) { var text = code; @@ -464,8 +493,11 @@ public interface ILogger refs) .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithNullableContextOptions(NullableContextOptions.Enable)); - // make sure we have valid syntax - Assert.Empty(compilation.GetDiagnostics(CancellationToken.None)); + if (checkDiags) + { + // make sure we have valid syntax + Assert.Empty(compilation.GetDiagnostics(CancellationToken.None)); + } var results = new List(); var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(compilation, (d) => { From 4424c8b950447680f688331af1f6dc63afe2a5b6 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 2 Dec 2020 06:59:41 -0800 Subject: [PATCH 056/121] More testing and tweaking --- .../gen/LoggingGenerator.Parser.cs | 21 +- .../Definitions.cs | 201 ++++++++++++++++++ .../GeneratedCodeTests.cs | 194 +---------------- .../GeneratorEmitTests.cs | 42 ++++ .../GeneratorParserTests.cs | 94 ++++---- ...Extensions.Logging.Generators.Tests.csproj | 2 +- .../RoslynTestUtils.cs | 184 ++++++++++++++++ 7 files changed, 480 insertions(+), 258 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index 9b50799372183..fb95c9abb03e4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -299,8 +299,6 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable where T : class + { + // normal public method + [LoggerMessage(0, LogLevel.Critical, "Message1")] + public static partial void M1(ILogger logger); + + // internal method + [LoggerMessage(1, LogLevel.Critical, "Message2")] + internal static partial void M2(ILogger logger); + + // private method + [LoggerMessage(2, LogLevel.Critical, "Message3")] + private static partial void M3(ILogger logger); + + // generic ILogger + [LoggerMessage(3, LogLevel.Critical, "Message4")] + private static partial void M4(ILogger logger); + + // random type method parameter + [LoggerMessage(4, LogLevel.Critical, "Message5")] + private static partial void M5(ILogger logger, System.Collections.IEnumerable items); + + // linefeeds and quotes in the message string + [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] + private static partial void M6(ILogger logger); + + // generic parameter + [LoggerMessage(6, LogLevel.Critical, "Message7\n\"\r")] + private static partial void M7(ILogger logger, T p1); + + // normal public method + [LoggerMessage(7, LogLevel.Critical, "Message8")] + private protected static partial void M8(ILogger logger); + + // internal method + [LoggerMessage(8, LogLevel.Critical, "Message9")] + internal protected static partial void M9(ILogger logger); + + // nullable parameter + [LoggerMessage(9, LogLevel.Critical, "Message10")] + internal static partial void M10(ILogger logger, string? optional); + + public static void Combo(ILogger logger, ILogger logger2) + { + M1(logger); + M2(logger); + M3(logger); + M4(logger2); + M5(logger, new string[] { "A" }); + M6(logger); + M8(logger); + M9(logger); + M10(logger, null); + } + } + + // test particular method signature variations are generated correctly + static partial class SignatureTests + { + // extension method + [LoggerMessage(10, LogLevel.Critical, "Message11")] + internal static partial void M11(this ILogger logger); + + public static void Combo(ILogger logger) + { + logger.M11(); + } + } + + partial class ArgTestExtensions + { + [LoggerMessage(0, LogLevel.Error, "M1")] + public static partial void Method1(ILogger logger); + + [LoggerMessage(1, LogLevel.Error, "M2")] + public static partial void Method2(ILogger logger, string p1); + + [LoggerMessage(2, LogLevel.Error, "M3 {p1} {p2}")] + public static partial void Method3(ILogger logger, string p1, int p2); + + [LoggerMessage(3, LogLevel.Error, "M4")] + public static partial void Method4(ILogger logger, InvalidOperationException p1); + + [LoggerMessage(4, LogLevel.Error, "M5")] + public static partial void Method5(ILogger logger, InvalidOperationException p1, InvalidOperationException p2); + + [LoggerMessage(5, LogLevel.Error, "M6")] + public static partial void Method6(ILogger logger, InvalidOperationException p1, int p2); + + [LoggerMessage(6, LogLevel.Error, "M7")] + public static partial void Method7(ILogger logger, int p1, InvalidOperationException p2); + + [LoggerMessage(7, LogLevel.Error, "M8")] + public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); + + [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] + public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); + } + + partial class ReadOnlyListExtensions + { + [LoggerMessage(0, LogLevel.Error, "M0")] + public static partial void M0(ILogger logger); + + [LoggerMessage(1, LogLevel.Error, "M1")] + public static partial void M1(ILogger logger, int p0); + + [LoggerMessage(2, LogLevel.Error, "M2")] + public static partial void M2(ILogger logger, int p0, int p1); + + [LoggerMessage(3, LogLevel.Error, "M3")] + public static partial void M3(ILogger logger, int p0, int p1, int p2); + + [LoggerMessage(4, LogLevel.Error, "M4")] + public static partial void M4(ILogger logger, int p0, int p1, int p2, int p3); + + [LoggerMessage(5, LogLevel.Error, "M5")] + public static partial void M5(ILogger logger, int p0, int p1, int p2, int p3, int p4); + + [LoggerMessage(6, LogLevel.Error, "M6")] + public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); + + [LoggerMessage(7, LogLevel.Error, "M7")] + public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); + } + + partial class LevelTestExtensions + { + [LoggerMessage(0, LogLevel.Trace, "M0")] + public static partial void M0(ILogger logger); + + [LoggerMessage(1, LogLevel.Debug, "M1")] + public static partial void M1(ILogger logger); + + [LoggerMessage(2, LogLevel.Information, "M2")] + public static partial void M2(ILogger logger); + + [LoggerMessage(3, LogLevel.Warning, "M3")] + public static partial void M3(ILogger logger); + + [LoggerMessage(4, LogLevel.Error, "M4")] + public static partial void M4(ILogger logger); + + [LoggerMessage(5, LogLevel.Critical, "M5")] + public static partial void M5(ILogger logger); + + [LoggerMessage(6, LogLevel.None, "M6")] + public static partial void M6(ILogger logger); + + [LoggerMessage(7, LogLevel.None, "M7")] + public static partial void M7(ILogger logger); + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index 0a53826e56515..cab3cd623b982 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -1,197 +1,11 @@ // © Microsoft Corporation. All rights reserved. -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Logging; -using Xunit; - -#pragma warning disable CA1801 // Review unused parameters - -// Used to test use outside of a namespace -partial class NoNamespace -{ - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - public static partial void CouldNotOpenSocket(ILogger logger, string hostName); -} - -namespace Level1 -{ - // used to test use inside a one-level namespace - partial class OneLevelNamespace - { - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - public static partial void CouldNotOpenSocket(ILogger logger, string hostName); - } -} - -namespace Level1 -{ - namespace Level2 - { - // used to test use inside a two-level namespace - partial class TwoLevelNamespace - { - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - public static partial void CouldNotOpenSocket(ILogger logger, string hostName); - } - } -} - namespace Microsoft.Extensions.Logging.Generators.Tests { - // test particular method signature variations are generated correctly - partial class SignatureTests where T : class - { - // normal public method - [LoggerMessage(0, LogLevel.Critical, "Message1")] - public static partial void M1(ILogger logger); - - // internal method - [LoggerMessage(1, LogLevel.Critical, "Message2")] - internal static partial void M2(ILogger logger); - - // private method - [LoggerMessage(2, LogLevel.Critical, "Message3")] - private static partial void M3(ILogger logger); - - // generic ILogger - [LoggerMessage(3, LogLevel.Critical, "Message4")] - private static partial void M4(ILogger logger); - - // random type method parameter - [LoggerMessage(4, LogLevel.Critical, "Message5")] - private static partial void M5(ILogger logger, System.Collections.IEnumerable items); - - // linefeeds and quotes in the message string - [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] - private static partial void M6(ILogger logger); - - // generic parameter - [LoggerMessage(6, LogLevel.Critical, "Message7\n\"\r")] - private static partial void M7(ILogger logger, T p1); - - // normal public method - [LoggerMessage(7, LogLevel.Critical, "Message8")] - private protected static partial void M8(ILogger logger); - - // internal method - [LoggerMessage(8, LogLevel.Critical, "Message9")] - internal protected static partial void M9(ILogger logger); - - // nullable parameter - [LoggerMessage(9, LogLevel.Critical, "Message10")] - internal static partial void M10(ILogger logger, string? optional); - - public static void Combo(ILogger logger, ILogger logger2) - { - M1(logger); - M2(logger); - M3(logger); - M4(logger2); - M5(logger, new string[] { "A" }); - M6(logger); - M8(logger); - M9(logger); - M10(logger, null); - } - } - - // test particular method signature variations are generated correctly - static partial class SignatureTests - { - // extension method - [LoggerMessage(10, LogLevel.Critical, "Message11")] - internal static partial void M11(this ILogger logger); - - public static void Combo(ILogger logger) - { - logger.M11(); - } - } - - partial class ArgTestExtensions - { - [LoggerMessage(0, LogLevel.Error, "M1")] - public static partial void Method1(ILogger logger); - - [LoggerMessage(1, LogLevel.Error, "M2")] - public static partial void Method2(ILogger logger, string p1); - - [LoggerMessage(2, LogLevel.Error, "M3 {p1} {p2}")] - public static partial void Method3(ILogger logger, string p1, int p2); - - [LoggerMessage(3, LogLevel.Error, "M4")] - public static partial void Method4(ILogger logger, InvalidOperationException p1); - - [LoggerMessage(4, LogLevel.Error, "M5")] - public static partial void Method5(ILogger logger, InvalidOperationException p1, InvalidOperationException p2); - - [LoggerMessage(5, LogLevel.Error, "M6")] - public static partial void Method6(ILogger logger, InvalidOperationException p1, int p2); - - [LoggerMessage(6, LogLevel.Error, "M7")] - public static partial void Method7(ILogger logger, int p1, InvalidOperationException p2); - - [LoggerMessage(7, LogLevel.Error, "M8")] - public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); - - [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] - public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); - } - - partial class ReadOnlyListExtensions - { - [LoggerMessage(0, LogLevel.Error, "M0")] - public static partial void M0(ILogger logger); - - [LoggerMessage(1, LogLevel.Error, "M1")] - public static partial void M1(ILogger logger, int p0); - - [LoggerMessage(2, LogLevel.Error, "M2")] - public static partial void M2(ILogger logger, int p0, int p1); - - [LoggerMessage(3, LogLevel.Error, "M3")] - public static partial void M3(ILogger logger, int p0, int p1, int p2); - - [LoggerMessage(4, LogLevel.Error, "M4")] - public static partial void M4(ILogger logger, int p0, int p1, int p2, int p3); - - [LoggerMessage(5, LogLevel.Error, "M5")] - public static partial void M5(ILogger logger, int p0, int p1, int p2, int p3, int p4); - - [LoggerMessage(6, LogLevel.Error, "M6")] - public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); - - [LoggerMessage(7, LogLevel.Error, "M7")] - public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); - } - - partial class LevelTestExtensions - { - [LoggerMessage(0, LogLevel.Trace, "M0")] - public static partial void M0(ILogger logger); - - [LoggerMessage(1, LogLevel.Debug, "M1")] - public static partial void M1(ILogger logger); - - [LoggerMessage(2, LogLevel.Information, "M2")] - public static partial void M2(ILogger logger); - - [LoggerMessage(3, LogLevel.Warning, "M3")] - public static partial void M3(ILogger logger); - - [LoggerMessage(4, LogLevel.Error, "M4")] - public static partial void M4(ILogger logger); - - [LoggerMessage(5, LogLevel.Critical, "M5")] - public static partial void M5(ILogger logger); - - [LoggerMessage(6, LogLevel.None, "M6")] - public static partial void M6(ILogger logger); - - [LoggerMessage(7, LogLevel.None, "M7")] - public static partial void M7(ILogger logger); - } + using Microsoft.Extensions.Logging; + using System; + using System.Collections.Generic; + using Xunit; public class GeneratedCodeTests { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs new file mode 100644 index 0000000000000..d562208392e20 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs @@ -0,0 +1,42 @@ +// © Microsoft Corporation. All rights reserved. + +namespace Microsoft.Extensions.Logging.Generators.Tests +{ + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using System.IO; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Xunit; + + public class GeneratorEmitTests + { + [Fact] + public async Task TestEmitter() + { + var testSourceCode = File.ReadAllText(@"..\..\..\Definitions.cs"); + + var proj = RoslynTestUtils.CreateTestProject() + .WithLoggingBoilerplate() + .WithDocument("Definitions.cs", testSourceCode); + + await proj.CommitChanges("CS8795").ConfigureAwait(false); + var comp = (await proj.GetCompilationAsync().ConfigureAwait(false))!; + + var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(comp, d => {}, CancellationToken.None); + var e = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Emitter(false); + + var allNodes = comp.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); + var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); + var lc = p.GetLogClasses(allClasses); + var generatedSource = e.Emit(lc, CancellationToken.None); + + Assert.True(!string.IsNullOrEmpty(generatedSource)); + proj = proj.WithDocument("log.cs", generatedSource); + + await RoslynTestUtils.AssertNoDiagnostic(proj).ConfigureAwait(false); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs index 23607382dcfba..f1e0b8675d050 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs @@ -17,7 +17,7 @@ public class GeneratorParserTests [Fact] public void InvalidMethodName() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -33,7 +33,7 @@ partial class C [Fact] public void InvalidMessage() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, """")] @@ -49,7 +49,7 @@ partial class C [Fact] public void InvalidParameterName() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -65,7 +65,7 @@ partial class C [Fact] public void NestedType() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { public partial class Nested @@ -84,7 +84,7 @@ public partial class Nested [Fact] public void RequiredTypes() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" namespace System { public class Object @@ -107,7 +107,7 @@ partial class C Assert.Single(d); Assert.Equal("LG0004", d[0].Id); - (lc, d) = TryCode(@" + (lc, d) = TryParser(@" partial class C { } @@ -117,7 +117,7 @@ partial class C Assert.Single(d); Assert.Equal("LG0004", d[0].Id); - (lc, d) = TryCode(@" + (lc, d) = TryParser(@" namespace Microsoft.Extensions.Logging { public sealed class LoggerMessageAttribute : System.Attribute {} @@ -135,7 +135,7 @@ partial class C [Fact] public void EventIdReuse() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -154,7 +154,7 @@ partial class C [Fact] public void MethodReturnType() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -172,7 +172,7 @@ partial class C [Fact] public void FirstArgILogger() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -188,7 +188,7 @@ partial class C [Fact] public void NotStatic() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -204,7 +204,7 @@ partial class C [Fact] public void NotPartial() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -220,7 +220,7 @@ static void M1(ILogger logger) {} [Fact] public void MethodGeneric() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -236,7 +236,7 @@ partial class C [Fact] public void Templates() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(1, LogLevel.Debug, ""M1"")] @@ -276,7 +276,7 @@ partial class C [Fact] public void Namespace() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" namespace Foo { partial class C @@ -292,7 +292,7 @@ partial class C Assert.Equal("C", lc[0].Name); Assert.Empty(d); - (lc, d) = TryCode(@" + (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -305,7 +305,7 @@ partial class C Assert.Equal("C", lc[0].Name); Assert.Empty(d); - (lc, d) = TryCode(@" + (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -322,7 +322,7 @@ partial class C [Fact] public void Generic() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -339,7 +339,7 @@ partial class C [Fact] public void EventName() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"", EventName = ""MyEvent"")] @@ -357,7 +357,7 @@ partial class C [Fact] public void Cancellation() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -372,7 +372,7 @@ partial class C [Fact] public void RandomAttribute() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" partial class C { [System.Obsolete(""Foo"")] @@ -387,7 +387,7 @@ partial class C [Fact] public void ExtensionMethod() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" static partial class C { [LoggerMessage(0, LogLevel.Debug, ""Hello"")] @@ -402,24 +402,28 @@ static partial class C [Fact] public void SourceErrors() { - var (lc, d) = TryCode(@" + var (lc, d) = TryParser(@" static partial class C { // bogus argument type [LoggerMessage(0, "", ""Hello"")] static partial void M1(ILogger logger); - // attribute applied to something other than a method - [LoggerMessage(0, "", ""Hello"")] - int field; - // missing parameter name - [LoggerMessage(0, LogLevel.Debug, ""Hello"")] + [LoggerMessage(1, LogLevel.Debug, ""Hello"")] static partial void M2(ILogger); // bogus parameter type - [LoggerMessage(0, LogLevel.Debug, ""Hello"")] - static partial void M2(XILogger logger); + [LoggerMessage(2, LogLevel.Debug, ""Hello"")] + static partial void M3(XILogger logger); + + // bogus enum value + [LoggerMessage(3, LogLevel.Foo, ""Hello"")] + static partial void M4(ILogger logger); + + // attribute applied to something other than a method + [LoggerMessage(4, "", ""Hello"")] + int M5; } ", checkDiags: false); @@ -427,7 +431,7 @@ static partial class C Assert.Empty(d); // should fail quietly on broken code } - private static (IReadOnlyList, IReadOnlyList) TryCode( + private static (IReadOnlyList, IReadOnlyList) TryParser( string code, bool wrap = true, bool inNamespace = true, @@ -451,33 +455,7 @@ private static (IReadOnlyList, IReadOnlyList (EventId, Level, Message) = (eventId, level, message); - public int EventId {{ get; set; }} - public string? EventName {{ get; set; }} - public LogLevel Level {{ get; set; }} - public string Message {{ get; set; }} - }} - - public interface ILogger - {{ - }} - }} + {RoslynTestUtils.LoggingBoilerplate} "; } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 120728f10e27d..4b8cb1fd6f44a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -8,7 +8,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs new file mode 100644 index 0000000000000..25b9ccba05cdc --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs @@ -0,0 +1,184 @@ +// © Microsoft Corporation. All rights reserved. + +namespace Microsoft.Extensions.Logging.Generators.Tests +{ + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.Text; + using System; + using System.IO; + using System.Reflection; + using System.Threading; + using System.Threading.Tasks; + using Xunit; + + static class RoslynTestUtils + { + public static Project CreateTestProject() + { +#pragma warning disable CA2000 // Dispose objects before losing scope + return new AdhocWorkspace() + .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create())) + .AddProject("Test", "test.dll", "C#") + .WithMetadataReferences(new[] { MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location) }) + .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithNullableContextOptions(NullableContextOptions.Enable)); +#pragma warning restore CA2000 // Dispose objects before losing scope + } + + public static void Dispose(this Project proj) + { + proj.Solution.Workspace.Dispose(); + } + + public static Task CommitChanges(this Project proj, params string[] ignorables) + { + Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution)); + return AssertNoDiagnostic(proj, ignorables); + } + + public static async Task AssertNoDiagnostic(this Project proj, params string[] ignorables) + { + foreach (var doc in proj.Documents) + { + var sm = await doc.GetSemanticModelAsync(CancellationToken.None).ConfigureAwait(false); + Assert.NotNull(sm); + + foreach (var d in sm!.GetDiagnostics()) + { + bool ignore = false; + if (ignorables != null) + { + foreach (var ig in ignorables) + { + if (d.Id == ig) + { + ignore = true; + break; + } + } + } + + Assert.True(ignore, d.ToString()); + } + } + } + + public static Project WithDocument(this Project proj, string name, string text) + { + return proj.AddDocument(name, text).Project; + } + + public const string LoggingBoilerplate = @" + namespace Microsoft.Extensions.Logging + { + using System; + + public enum LogLevel + { + Trace, + Debug, + Information, + Warning, + Error, + Critical, + None, + } + + [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] + public sealed class LoggerMessageAttribute : System.Attribute + { + public LoggerMessageAttribute(int eventId, LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); + public int EventId { get; set; } + public string? EventName { get; set; } + public LogLevel Level { get; set; } + public string Message { get; set; } + } + + public interface ILogger + { + } + + public interface ILogger : ILogger + { + } + + public struct EventId + { + public EventId(int id, string name) {} + } + + public static class LoggerExtensions + { + public static void Log(this ILogger logger, LogLevel logLevel, Exception exception, string message, params object[] args){} + public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, string message, params object[] args){} + public static void Log(this ILogger logger, LogLevel logLevel, string message, params object[] args){} + public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, Exception exception, string message, params object[] args){} + public static void LogCritical(this ILogger logger, string message, params object[] args){} + public static void LogCritical(this ILogger logger, Exception exception, string message, params object[] args){} + public static void LogCritical(this ILogger logger, EventId eventId, string message, params object[] args){} + public static void LogCritical(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} + public static void LogDebug(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} + public static void LogDebug(this ILogger logger, EventId eventId, string message, params object[] args){} + public static void LogDebug(this ILogger logger, Exception exception, string message, params object[] args){} + public static void LogDebug(this ILogger logger, string message, params object[] args){} + public static void LogError(this ILogger logger, string message, params object[] args){} + public static void LogError(this ILogger logger, Exception exception, string message, params object[] args){} + public static void LogError(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} + public static void LogError(this ILogger logger, EventId eventId, string message, params object[] args){} + public static void LogInformation(this ILogger logger, EventId eventId, string message, params object[] args){} + public static void LogInformation(this ILogger logger, Exception exception, string message, params object[] args){} + public static void LogInformation(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} + public static void LogInformation(this ILogger logger, string message, params object[] args){} + public static void LogTrace(this ILogger logger, string message, params object[] args){} + public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args){} + public static void LogTrace(this ILogger logger, EventId eventId, string message, params object[] args){} + public static void LogTrace(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} + public static void LogWarning(this ILogger logger, EventId eventId, string message, params object[] args){} + public static void LogWarning(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} + public static void LogWarning(this ILogger logger, string message, params object[] args){} + public static void LogWarning(this ILogger logger, Exception exception, string message, params object[] args){} + } + } + "; + + public static Project WithLoggingBoilerplate(this Project proj) + { + return proj.AddDocument("boilerplate.cs", LoggingBoilerplate).Project; + } + + public static Document FindDocument(this Project proj, string name) + { + foreach (var doc in proj.Documents) + { + if (doc.Name == name) + { + return doc; + } + } + + throw new FileNotFoundException(name); + } + + /// + /// Looks for /*N+*/ and /*-N*/ markers in a string and creates a TextSpan containing the enclosed text. + /// + public static TextSpan MakeSpan(string text, int spanNum) + { + int start = text.IndexOf($"/*{spanNum}+*/", StringComparison.Ordinal); + if (start < 0) + { + throw new ArgumentOutOfRangeException(nameof(spanNum)); + } + start += 6; + + int end = text.IndexOf($"/*-{spanNum}*/", StringComparison.Ordinal); + if (end < 0) + { + throw new ArgumentOutOfRangeException(nameof(spanNum)); + } + end -= 1; + + return new TextSpan(start, end - start); + } + } +} From bb33d1c6cd7f1223167031bda6da56750c4deb89 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 2 Dec 2020 16:14:12 -0800 Subject: [PATCH 057/121] More coverage, more fixes --- .../gen/LoggingGenerator.Emit.cs | 17 +++++++-------- .../Definitions.cs | 11 +++++++++- .../GeneratedCodeTests.cs | 21 ++++++++++++++++++- .../GeneratorEmitTests.cs | 21 +++++++++++-------- 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs index fc380212ec9cc..8b33804225fad 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs @@ -86,7 +86,7 @@ partial class {lc.Name} {lc.Constraints} private string GenFormatFunc(LoggerMethod lm) { - if (lm.Parameters.Count == 0 || !lm.MessageHasTemplates) + if (!lm.MessageHasTemplates) { return string.Empty; } @@ -95,7 +95,11 @@ private string GenFormatFunc(LoggerMethod lm) try { string typeName; - if (lm.Parameters.Count == 1) + if (lm.Parameters.Count == 0) + { + typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder"; + } + else if (lm.Parameters.Count == 1) { typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; sb.Append($"var {lm.Parameters[0].Name} = __holder.Value;\n"); @@ -209,14 +213,7 @@ private string GenLogMethod(LoggerMethod lm) string formatFunc; if (lm.MessageHasTemplates) { - if (lm.Parameters.Count == 0) - { - formatFunc = "Microsoft.Extensions.Logging.LogStateHolder.Format"; - } - else - { - formatFunc = $"__{lm.Name}FormatFunc"; - } + formatFunc = $"__{lm.Name}FormatFunc"; } else { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs index 7816af2321e0f..3ed568900bf99 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs @@ -143,6 +143,9 @@ partial class ArgTestExtensions [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); + + [LoggerMessage(9, LogLevel.Error, "M10")] + public static partial void Method10(ILogger logger, int p1); } partial class ReadOnlyListExtensions @@ -195,7 +198,13 @@ partial class LevelTestExtensions [LoggerMessage(6, LogLevel.None, "M6")] public static partial void M6(ILogger logger); - [LoggerMessage(7, LogLevel.None, "M7")] + [LoggerMessage(7, (LogLevel)42, "M7")] public static partial void M7(ILogger logger); } + + partial class EventNameTestExtensions + { + [LoggerMessage(0, LogLevel.Trace, "M0", EventName = "CustomEventName")] + public static partial void M0(ILogger logger); + } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index cab3cd623b982..65efe2c409774 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -104,6 +104,11 @@ public void ArgTest() ArgTestExtensions.Method9(logger, 1, 2, 3, 4, 5, 6, 7); Assert.Equal("M9 1 2 3 4 5 6 7", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ArgTestExtensions.Method10(logger, 1); + Assert.Equal("M10", logger.LastFormattedString); + Assert.Equal(1, logger.CallCount); } [Fact] @@ -227,8 +232,22 @@ public void LevelTests() LevelTestExtensions.M7(logger); Assert.Null(logger.LastException); Assert.Equal("M7", logger.LastFormattedString); - Assert.Equal(LogLevel.None, logger.LastLogLevel); + Assert.Equal((LogLevel)42, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + } + + [Fact] + public void EventNameTests() + { + var logger = new MockLogger(); + + logger.Reset(); + EventNameTestExtensions.M0(logger); + Assert.Null(logger.LastException); + Assert.Equal("M0", logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); + Assert.Equal("CustomEventName", logger.LastEventId.Name); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs index d562208392e20..64e3922ec295d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs @@ -25,18 +25,21 @@ public async Task TestEmitter() await proj.CommitChanges("CS8795").ConfigureAwait(false); var comp = (await proj.GetCompilationAsync().ConfigureAwait(false))!; - var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(comp, d => {}, CancellationToken.None); - var e = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Emitter(false); + for (int i = 0; i < 2; i++) + { + var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(comp, d => { }, CancellationToken.None); + var e = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Emitter(i == 0); - var allNodes = comp.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); - var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); - var lc = p.GetLogClasses(allClasses); - var generatedSource = e.Emit(lc, CancellationToken.None); + var allNodes = comp.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); + var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); + var lc = p.GetLogClasses(allClasses); - Assert.True(!string.IsNullOrEmpty(generatedSource)); - proj = proj.WithDocument("log.cs", generatedSource); + var generatedSource = e.Emit(lc, CancellationToken.None); + Assert.True(!string.IsNullOrEmpty(generatedSource)); - await RoslynTestUtils.AssertNoDiagnostic(proj).ConfigureAwait(false); + generatedSource = e.Emit(lc, new CancellationToken(true)); + Assert.True(string.IsNullOrEmpty(generatedSource)); + } } } } From c89bb35b98f1f5ed9a2a2052a5d47d7e73f02ef1 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Wed, 9 Dec 2020 14:22:05 -0800 Subject: [PATCH 058/121] Relocate using statements --- .../gen/LoggingGenerator.Emit.cs | 8 ++++---- .../gen/LoggingGenerator.Parser.cs | 18 ++++++++--------- .../gen/LoggingGenerator.cs | 12 +++++------ .../GeneratedCodeTests.cs | 10 +++++----- .../GeneratorEmitTests.cs | 18 ++++++++--------- .../GeneratorParserTests.cs | 20 +++++++++---------- .../MockLogger.cs | 4 ++-- .../RoslynTestUtils.cs | 20 +++++++++---------- 8 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs index 8b33804225fad..db9492025a7e2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs @@ -1,11 +1,11 @@ // © Microsoft Corporation. All rights reserved. +using System.Collections.Generic; +using System.Text; +using System.Threading; + namespace Microsoft.Extensions.Logging.Generators { - using System.Collections.Generic; - using System.Text; - using System.Threading; - public partial class LoggingGenerator { internal class Emitter diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs index fb95c9abb03e4..17df38f4af893 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs @@ -1,19 +1,19 @@ // © Microsoft Corporation. All rights reserved. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + [assembly: System.Resources.NeutralResourcesLanguage("en-us")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.Extensions.Logging.Generators.Tests")] namespace Microsoft.Extensions.Logging.Generators { - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Threading; - public partial class LoggingGenerator { internal class Parser diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs index aafc9eea7adfe..b0d459dc7abd8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.cs @@ -1,13 +1,13 @@ // © Microsoft Corporation. All rights reserved. +using System.Collections.Generic; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + namespace Microsoft.Extensions.Logging.Generators { - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using Microsoft.CodeAnalysis.Text; - using System.Collections.Generic; - using System.Text; - [Generator] public partial class LoggingGenerator : ISourceGenerator { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs index 65efe2c409774..be33fd4720980 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs @@ -1,12 +1,12 @@ // © Microsoft Corporation. All rights reserved. +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Xunit; + namespace Microsoft.Extensions.Logging.Generators.Tests { - using Microsoft.Extensions.Logging; - using System; - using System.Collections.Generic; - using Xunit; - public class GeneratedCodeTests { [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs index 64e3922ec295d..7b1889fb82fb9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs @@ -1,16 +1,16 @@ // © Microsoft Corporation. All rights reserved. +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Xunit; + namespace Microsoft.Extensions.Logging.Generators.Tests { - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using System.IO; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using Xunit; - public class GeneratorEmitTests { [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs index f1e0b8675d050..3be3cee2242fa 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs @@ -1,17 +1,17 @@ // © Microsoft Corporation. All rights reserved. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Xunit; + namespace Microsoft.Extensions.Logging.Generators.Tests { - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Threading; - using Xunit; - public class GeneratorParserTests { [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs index aae161333e114..b5383e0ac30a4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -1,9 +1,9 @@ // © Microsoft Corporation. All rights reserved. +using System; + namespace Microsoft.Extensions.Logging.Generators.Tests { - using System; - /// /// A logger which captures the last log state logged to it. /// diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs index 25b9ccba05cdc..bd632da6870a0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs @@ -1,17 +1,17 @@ // © Microsoft Corporation. All rights reserved. +using System; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; +using Xunit; + namespace Microsoft.Extensions.Logging.Generators.Tests { - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.Text; - using System; - using System.IO; - using System.Reflection; - using System.Threading; - using System.Threading.Tasks; - using Xunit; - static class RoslynTestUtils { public static Project CreateTestProject() From 755f3ff3136293da1baea1346360f431ac338163 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Thu, 10 Dec 2020 20:47:20 -0800 Subject: [PATCH 059/121] Cleanup analyzer story --- .../gen/Microsoft.Extensions.Logging.Generators.csproj | 5 +++-- .../Microsoft.Extensions.Logging.Generators.Tests.csproj | 5 +---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 182d87d647a79..2d08d7988f545 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -5,14 +5,15 @@ 9.0 enable true + true - - + all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 4b8cb1fd6f44a..2a90027b36f93 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -5,14 +5,11 @@ enable true true + true - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - From 225b63dacae9d35febb77bf53910d885991360ac Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 26 Dec 2020 10:44:47 -0800 Subject: [PATCH 060/121] Refactoring and cleanup --- .../gen/DiagDescriptors.cs | 97 ++++++++++++++ ...Emit.cs => LoggerMessageGenerator.Emit.cs} | 2 +- ...er.cs => LoggerMessageGenerator.Parser.cs} | 120 ++---------------- ...Generator.cs => LoggerMessageGenerator.cs} | 4 +- ....cs => LoggerMessageGeneratedCodeTests.cs} | 2 +- ....cs => LoggerMessageGeneratorEmitTests.cs} | 6 +- ...s => LoggerMessageGeneratorParserTests.cs} | 6 +- .../RoslynTestUtils.cs | 11 +- 8 files changed, 125 insertions(+), 123 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs rename src/libraries/Microsoft.Extensions.Logging/gen/{LoggingGenerator.Emit.cs => LoggerMessageGenerator.Emit.cs} (99%) rename src/libraries/Microsoft.Extensions.Logging/gen/{LoggingGenerator.Parser.cs => LoggerMessageGenerator.Parser.cs} (75%) rename src/libraries/Microsoft.Extensions.Logging/gen/{LoggingGenerator.cs => LoggerMessageGenerator.cs} (91%) rename src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/{GeneratedCodeTests.cs => LoggerMessageGeneratedCodeTests.cs} (99%) rename src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/{GeneratorEmitTests.cs => LoggerMessageGeneratorEmitTests.cs} (90%) rename src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/{GeneratorParserTests.cs => LoggerMessageGeneratorParserTests.cs} (98%) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs new file mode 100644 index 0000000000000..2327d8c6256c8 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs @@ -0,0 +1,97 @@ +// © Microsoft Corporation. All rights reserved. + +using Microsoft.CodeAnalysis; + +namespace Microsoft.Extensions.Logging.Generators +{ + internal static class DiagDescriptors + { + public static DiagnosticDescriptor ErrorInvalidMethodName { get; } = new ( + id: "LG0000", + title: Resources.ErrorInvalidMethodNameTitle, + messageFormat: Resources.ErrorInvalidMethodNameMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorInvalidMessage { get; } = new ( + id: "LG0001", + title: Resources.ErrorInvalidMessageTitle, + messageFormat: Resources.ErrorInvalidMessageMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorInvalidParameterName { get; } = new ( + id: "LG0002", + title: Resources.ErrorInvalidParameterNameTitle, + messageFormat: Resources.ErrorInvalidParameterNameMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorNestedType { get; } = new ( + id: "LG0003", + title: Resources.ErrorNestedTypeTitle, + messageFormat: Resources.ErrorNestedTypeMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorMissingRequiredType { get; } = new ( + id: "LG0004", + title: Resources.ErrorMissingRequiredTypeTitle, + messageFormat: Resources.ErrorMissingRequiredTypeMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorEventIdReuse { get; } = new ( + id: "LG0005", + title: Resources.ErrorEventIdReuseTitle, + messageFormat: Resources.ErrorEventIdReuseMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorInvalidMethodReturnType { get; } = new ( + id: "LG0006", + title: Resources.ErrorInvalidMethodReturnTypeTitle, + messageFormat: Resources.ErrorInvalidMethodReturnTypeMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorFirstArgMustBeILogger { get; } = new ( + id: "LG0007", + title: Resources.ErrorFirstArgMustBeILoggerTitle, + messageFormat: Resources.ErrorFirstArgMustBeILoggerMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorNotStaticMethod { get; } = new ( + id: "LG0008", + title: Resources.ErrorNotStaticMethodTitle, + messageFormat: Resources.ErrorNotStaticMethodMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorNotPartialMethod { get; } = new ( + id: "LG0009", + title: Resources.ErrorNotPartialMethodTitle, + messageFormat: Resources.ErrorNotPartialMethodMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorMethodIsGeneric { get; } = new ( + id: "LG0010", + title: Resources.ErrorMethodIsGenericTitle, + messageFormat: Resources.ErrorMethodIsGenericMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emit.cs similarity index 99% rename from src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs rename to src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emit.cs index db9492025a7e2..d6851877ad734 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Emit.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emit.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Logging.Generators { - public partial class LoggingGenerator + public partial class LoggerMessageGenerator { internal class Emitter { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs similarity index 75% rename from src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs rename to src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 17df38f4af893..ebe8f57abfc23 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -14,102 +14,10 @@ namespace Microsoft.Extensions.Logging.Generators { - public partial class LoggingGenerator + public partial class LoggerMessageGenerator { internal class Parser { - private const string DiagnosticCategory = "LoggingGenerator"; - -#pragma warning disable RS2008 // Enable analyzer release tracking - - private static readonly DiagnosticDescriptor ErrorInvalidMethodName = new( - id: "LG0000", - title: Resources.ErrorInvalidMethodNameTitle, - messageFormat: Resources.ErrorInvalidMethodNameMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorInvalidMessage = new( - id: "LG0001", - title: Resources.ErrorInvalidMessageTitle, - messageFormat: Resources.ErrorInvalidMessageMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorInvalidParameterName = new( - id: "LG0002", - title: Resources.ErrorInvalidParameterNameTitle, - messageFormat: Resources.ErrorInvalidParameterNameMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorNestedType = new( - id: "LG0003", - title: Resources.ErrorNestedTypeTitle, - messageFormat: Resources.ErrorNestedTypeMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorMissingRequiredType = new( - id: "LG0004", - title: Resources.ErrorMissingRequiredTypeTitle, - messageFormat: Resources.ErrorMissingRequiredTypeMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorEventIdReuse = new( - id: "LG0005", - title: Resources.ErrorEventIdReuseTitle, - messageFormat: Resources.ErrorEventIdReuseMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorInvalidMethodReturnType = new( - id: "LG0006", - title: Resources.ErrorInvalidMethodReturnTypeTitle, - messageFormat: Resources.ErrorInvalidMethodReturnTypeMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorFirstArgMustBeILogger = new( - id: "LG0007", - title: Resources.ErrorFirstArgMustBeILoggerTitle, - messageFormat: Resources.ErrorFirstArgMustBeILoggerMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorNotStaticMethod = new( - id: "LG0008", - title: Resources.ErrorNotStaticMethodTitle, - messageFormat: Resources.ErrorNotStaticMethodMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorNotPartialMethod = new( - id: "LG0009", - title: Resources.ErrorNotPartialMethodTitle, - messageFormat: Resources.ErrorNotPartialMethodMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor ErrorMethodIsGeneric = new( - id: "LG0010", - title: Resources.ErrorMethodIsGenericTitle, - messageFormat: Resources.ErrorMethodIsGenericMessage, - category: DiagnosticCategory, - DiagnosticSeverity.Error, - isEnabledByDefault: true); - private readonly CancellationToken _cancellationToken; private readonly Compilation _compilation; private readonly Action _reportDiagnostic; @@ -131,21 +39,21 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable 0) { // we don't currently support generic methods - Diag(ErrorMethodIsGeneric, method.Identifier.GetLocation()); + Diag(DiagDescriptors.ErrorMethodIsGeneric, method.Identifier.GetLocation()); keep = false; } @@ -264,20 +172,20 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable public void Initialize(GeneratorInitializationContext context) @@ -38,7 +38,7 @@ public void Execute(GeneratorExecutionContext context) var logClasses = p.GetLogClasses(receiver.ClassDeclarations); var result = e.Emit(logClasses, context.CancellationToken); - context.AddSource(nameof(LoggingGenerator), SourceText.From(result, Encoding.UTF8)); + context.AddSource(nameof(LoggerMessageGenerator), SourceText.From(result, Encoding.UTF8)); } private sealed class SyntaxReceiver : ISyntaxReceiver diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs similarity index 99% rename from src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs rename to src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index be33fd4720980..dcb42f75e30ad 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests { - public class GeneratedCodeTests + public class LoggerMessageGeneratedCodeTests { [Fact] public void BasicTests() diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitTests.cs similarity index 90% rename from src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs rename to src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitTests.cs index 7b1889fb82fb9..193c089634f96 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorEmitTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitTests.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests { - public class GeneratorEmitTests + public class LoggerMessageGeneratorEmitTests { [Fact] public async Task TestEmitter() @@ -27,8 +27,8 @@ public async Task TestEmitter() for (int i = 0; i < 2; i++) { - var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(comp, d => { }, CancellationToken.None); - var e = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Emitter(i == 0); + var p = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Parser(comp, d => { }, CancellationToken.None); + var e = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Emitter(i == 0); var allNodes = comp.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs similarity index 98% rename from src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs rename to src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 3be3cee2242fa..6b4deca103aa5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/GeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests { - public class GeneratorParserTests + public class LoggerMessageGeneratorParserTests { [Fact] public void InvalidMethodName() @@ -431,7 +431,7 @@ static partial class C Assert.Empty(d); // should fail quietly on broken code } - private static (IReadOnlyList, IReadOnlyList) TryParser( + private static (IReadOnlyList, IReadOnlyList) TryParser( string code, bool wrap = true, bool inNamespace = true, @@ -478,7 +478,7 @@ private static (IReadOnlyList, IReadOnlyList(); - var p = new Microsoft.Extensions.Logging.Generators.LoggingGenerator.Parser(compilation, (d) => { + var p = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Parser(compilation, (d) => { results.Add(d); }, cancellationToken); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs index bd632da6870a0..3995c4b348d61 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs @@ -46,15 +46,12 @@ public static async Task AssertNoDiagnostic(this Project proj, params string[] i foreach (var d in sm!.GetDiagnostics()) { bool ignore = false; - if (ignorables != null) + foreach (var ig in ignorables) { - foreach (var ig in ignorables) + if (d.Id == ig) { - if (d.Id == ig) - { - ignore = true; - break; - } + ignore = true; + break; } } From 139e5bb65f1c558840238d00bc4c2b640321ff2f Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 28 Dec 2020 20:14:05 -0800 Subject: [PATCH 061/121] Get coverage to 100% --- ...t.cs => LoggerMessageGenerator.Emitter.cs} | 174 +++++---- .../gen/LoggerMessageGenerator.Parser.cs | 341 +++++++++--------- .../gen/LoggerMessageGenerator.cs | 21 +- ...osoft.Extensions.Logging.Generators.csproj | 14 +- .../gen/Resources.Designer.cs | 8 +- .../gen/Resources.resx | 8 +- .../Definitions.cs | 70 ++-- .../LoggerMessageGeneratedCodeTests.cs | 56 +-- ... => LoggerMessageGeneratorEmitterTests.cs} | 13 +- .../LoggerMessageGeneratorParserTests.cs | 35 +- ...Extensions.Logging.Generators.Test.csproj} | 0 .../MockLogger.cs | 10 +- .../RoslynTestUtils.cs | 31 +- 13 files changed, 414 insertions(+), 367 deletions(-) rename src/libraries/Microsoft.Extensions.Logging/gen/{LoggerMessageGenerator.Emit.cs => LoggerMessageGenerator.Emitter.cs} (62%) rename src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/{LoggerMessageGeneratorEmitTests.cs => LoggerMessageGeneratorEmitterTests.cs} (70%) rename src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/{Microsoft.Extensions.Logging.Generators.Tests.csproj => Microsoft.Extensions.Logging.Generators.Test.csproj} (100%) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emit.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs similarity index 62% rename from src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emit.cs rename to src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index d6851877ad734..677dcc28c2793 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emit.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -1,4 +1,4 @@ -// © Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. using System.Collections.Generic; using System.Text; @@ -12,9 +12,11 @@ internal class Emitter { // The maximum arity of the LogStateHolder-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation // for the array and boxing of all logging method arguments. - const int MaxStateHolderArity = 6; + private const int MaxStateHolderArity = 6; + private const int MinStateHolderWithNameArray = 2; + private const string StateHolderNamespace = "Microsoft.Extensions.Logging.Internal"; - private readonly Stack _builders = new(); + private readonly Stack _builders = new (); private readonly bool _pascalCaseArguments; public Emitter(bool pascalCaseArguments) @@ -27,15 +29,13 @@ public string Emit(IReadOnlyList logClasses, CancellationToken canc var sb = GetStringBuilder(); try { + _ = sb.Append("// \n"); + _ = sb.Append("#nullable enable\n"); + foreach (var lc in logClasses) { - if (cancellationToken.IsCancellationRequested) - { - // stop any additional work - break; - } - - sb.Append(GenType(lc)); + cancellationToken.ThrowIfCancellationRequested(); + _ = sb.Append(GenType(lc)); } return sb.ToString(); @@ -46,6 +46,14 @@ public string Emit(IReadOnlyList logClasses, CancellationToken canc } } + private static string EscapeMessageString(string message) + { + return message + .Replace("\n", "\\n") + .Replace("\r", "\\r") + .Replace("\"", "\\\""); + } + private string GenType(LoggerClass lc) { var sb = GetStringBuilder(); @@ -53,9 +61,17 @@ private string GenType(LoggerClass lc) { foreach (var lm in lc.Methods) { - sb.Append(GenFormatFunc(lm)); - sb.Append(GenNameArray(lm)); - sb.Append(GenLogMethod(lm)); + _ = sb.Append(GenNameArray(lm)); + } + + foreach (var lm in lc.Methods) + { + _ = sb.Append(GenFormatFunc(lm)); + } + + foreach (var lm in lc.Methods) + { + _ = sb.Append(GenLogMethod(lm)); } if (string.IsNullOrWhiteSpace(lc.Namespace)) @@ -65,7 +81,7 @@ partial class {lc.Name} {lc.Constraints} {{ {sb} }} - "; + "; } return $@" @@ -95,54 +111,53 @@ private string GenFormatFunc(LoggerMethod lm) try { string typeName; - if (lm.Parameters.Count == 0) + if (lm.Parameters.Count == 1) { - typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder"; - } - else if (lm.Parameters.Count == 1) - { - typeName = $"global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>"; - sb.Append($"var {lm.Parameters[0].Name} = __holder.Value;\n"); + typeName = $"global::{StateHolderNamespace}.LogStateHolder<{lm.Parameters[0].Type}>"; + _ = sb.Append($" var {lm.Parameters[0].Name} = _holder.Value;\n"); } else if (lm.Parameters.Count > MaxStateHolderArity) { - typeName = "global::Microsoft.Extensions.Logging.LogStateHolderN"; + typeName = $"global::{StateHolderNamespace}.LogStateHolderN"; var index = 0; foreach (var p in lm.Parameters) { - sb.Append($"var {p.Name} = __holder[{index++}].Value;\n"); + _ = sb.Append($" var {p.Name} = _holder[{index++}].Value;\n"); } } else { - sb.Append("global::Microsoft.Extensions.Logging.LogStateHolder<"); + _ = sb.Append($"global::{StateHolderNamespace}.LogStateHolder<"); foreach (var p in lm.Parameters) { if (p != lm.Parameters[0]) { - sb.Append(", "); + _ = sb.Append(", "); } - sb.Append(p.Type); + _ = sb.Append(p.Type); } - sb.Append('>'); + + _ = sb.Append('>'); typeName = sb.ToString(); - sb.Clear(); + _ = sb.Clear(); var index = 1; foreach (var p in lm.Parameters) { - sb.Append($"var {p.Name} = __holder.Value{index++};\n"); + _ = sb.Append($" var {p.Name} = _holder.Value{index++};\n"); } } - return $@"private static readonly global::System.Func<{typeName}, global::System.Exception?, string> __{lm.Name}FormatFunc = (__holder, _) => - {{ - {sb} - return $""{EscapeMessageString(lm.Message)}""; - }}; + return $@" + [System.Runtime.CompilerServices.CompilerGenerated] + private static readonly global::System.Func<{typeName}, global::System.Exception?, string> _format{lm.Name} = (_holder, _) => + {{ +{sb} + return $""{EscapeMessageString(lm.Message)}""; + }}; "; } finally @@ -153,7 +168,7 @@ private string GenFormatFunc(LoggerMethod lm) private string GenNameArray(LoggerMethod lm) { - if (lm.Parameters.Count is < 2 or > MaxStateHolderArity) + if (lm.Parameters.Count is < MinStateHolderWithNameArray or > MaxStateHolderArity) { return string.Empty; } @@ -161,13 +176,14 @@ private string GenNameArray(LoggerMethod lm) var sb = GetStringBuilder(); try { - sb.Append($"private static readonly string[] __{lm.Name}Names = new []{{"); + _ = sb.Append($"\n [System.Runtime.CompilerServices.CompilerGenerated]\n"); + _ = sb.Append($" private static readonly string[] _names{lm.Name} = new[] {{ "); foreach (var p in lm.Parameters) { - sb.Append($"\"{NormalizeArgumentName(p.Name)}\", "); + _ = sb.Append($"\"{NormalizeArgumentName(p.Name)}\", "); } - sb.Append("};"); + _ = sb.Append("};\n"); return sb.ToString(); } finally @@ -213,27 +229,30 @@ private string GenLogMethod(LoggerMethod lm) string formatFunc; if (lm.MessageHasTemplates) { - formatFunc = $"__{lm.Name}FormatFunc"; + formatFunc = $"_format{lm.Name}"; } else { formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; } +#pragma warning disable S103 // Lines should not be too long return $@" - {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} __logger{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) - {{ - if (__logger.IsEnabled({level})) - {{ - __logger.Log( - {level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - {GenHolder(lm, formatFunc)}, - {exceptionArg}, - {formatFunc}); - }} - }} - "; + [System.Runtime.CompilerServices.CompilerGenerated] + {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} {lm.LoggerName}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {{ + if ({lm.LoggerName}.IsEnabled({level})) + {{ + {lm.LoggerName}.Log( + {level}, + new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), + {GenHolder(lm, formatFunc)}, + {exceptionArg}, + {formatFunc}); + }} + }} + "; +#pragma warning restore S103 // Lines should not be too long } private string GenParameters(LoggerMethod lm) @@ -245,10 +264,10 @@ private string GenParameters(LoggerMethod lm) { if (p != lm.Parameters[0]) { - sb.Append(", "); + _ = sb.Append(", "); } - sb.Append($"{p.Type} {p.Name}"); + _ = sb.Append($"{p.Type} {p.Name}"); } return sb.ToString(); @@ -263,12 +282,13 @@ private string GenHolder(LoggerMethod lm, string formatFunc) { if (lm.Parameters.Count == 0) { - return "new global::Microsoft.Extensions.Logging.LogStateHolder()"; + return $"default(global::{StateHolderNamespace}.LogStateHolder)"; } if (lm.Parameters.Count == 1) { - return $"new global::Microsoft.Extensions.Logging.LogStateHolder<{lm.Parameters[0].Type}>({formatFunc}, \"{NormalizeArgumentName(lm.Parameters[0].Name)}\", {lm.Parameters[0].Name})"; + return $"new global::{StateHolderNamespace}.LogStateHolder<{lm.Parameters[0].Type}>" + + $"({formatFunc}, \"{NormalizeArgumentName(lm.Parameters[0].Name)}\", {lm.Parameters[0].Name})"; } var sb = GetStringBuilder(); @@ -276,13 +296,13 @@ private string GenHolder(LoggerMethod lm, string formatFunc) { if (lm.Parameters.Count > MaxStateHolderArity) { - sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolderN({formatFunc}, new global::System.Collections.Generic.KeyValuePair[]{{"); + _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolderN({formatFunc}, new global::System.Collections.Generic.KeyValuePair[] {{ "); foreach (var p in lm.Parameters) { - sb.Append($"new(\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); + _ = sb.Append($"new (\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); } - sb.Append("})"); + _ = sb.Append("})"); } else { @@ -290,27 +310,29 @@ private string GenHolder(LoggerMethod lm, string formatFunc) { if (p != lm.Parameters[0]) { - sb.Append(", "); + _ = sb.Append(", "); } - sb.Append(p.Type); + _ = sb.Append(p.Type); } + var tp = sb.ToString(); - sb.Clear(); - sb.Append($"new global::Microsoft.Extensions.Logging.LogStateHolder<{tp}>({formatFunc}, __{lm.Name}Names, "); + _ = sb.Clear(); + _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolder<{tp}>({formatFunc}, _names{lm.Name}, "); foreach (var p in lm.Parameters) { if (p != lm.Parameters[0]) { - sb.Append(", "); + _ = sb.Append(", "); } - sb.Append(p.Name); + _ = sb.Append(p.Name); } - sb.Append(')'); + _ = sb.Append(')'); } + return sb.ToString(); } finally @@ -329,8 +351,8 @@ private string NormalizeArgumentName(string name) var sb = GetStringBuilder(); try { - sb.Append(char.ToUpperInvariant(name[0])); - sb.Append(name, 1, name.Length - 1); + _ = sb.Append(char.ToUpperInvariant(name[0])). + Append(name, 1, name.Length - 1); return sb.ToString(); } finally @@ -339,24 +361,18 @@ private string NormalizeArgumentName(string name) } } - private static string EscapeMessageString(string message) - { - return message - .Replace("\n", "\\n") - .Replace("\r", "\\r") - .Replace("\"", "\\\""); - } - - // our own cheezy object pool since we can't use the .NET core version + // our own cheezy object pool since we can't use the .NET core version (since this code runs in legacy .NET framework) private StringBuilder GetStringBuilder() { + const int DefaultStringBuilderCapacity = 1024; + if (_builders.Count == 0) { - return new StringBuilder(1024); + return new StringBuilder(DefaultStringBuilderCapacity); } var sb = _builders.Pop(); - sb.Clear(); + _ = sb.Clear(); return sb; } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index ebe8f57abfc23..2f44d70cf162e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -9,9 +9,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -[assembly: System.Resources.NeutralResourcesLanguage("en-us")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.Extensions.Logging.Generators.Tests")] - namespace Microsoft.Extensions.Logging.Generators { public partial class LoggerMessageGenerator @@ -34,19 +31,25 @@ public Parser(Compilation compilation, Action reportDiagnostic, Canc /// public IReadOnlyList GetLogClasses(IEnumerable classes) { + const string LoggerMessageAttribute = "Microsoft.Extensions.Logging.LoggerMessageAttribute"; + const int LoggerMessageAttrEventIdArg = 0; + const int LoggerMessageAttrLevelArg = 1; + const int LoggerMessageAttrMessageArg = 2; + const int LoggerMessageAttrEventNameArg = 3; + var results = new List(); - var exSymbol = _compilation.GetTypeByMetadataName("System.Exception"); - if (exSymbol == null) + var exceptionSymbol = _compilation.GetTypeByMetadataName("System.Exception"); + if (exceptionSymbol == null) { Diag(DiagDescriptors.ErrorMissingRequiredType, null, "System.Exception"); return results; } - var loggerMessageAttribute = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); + var loggerMessageAttribute = _compilation.GetTypeByMetadataName(LoggerMessageAttribute); if (loggerMessageAttribute is null) { - Diag(DiagDescriptors.ErrorMissingRequiredType, null, "Microsoft.Extensions.Logging.LoggerMessageAttribute"); + Diag(DiagDescriptors.ErrorMissingRequiredType, null, LoggerMessageAttribute); return results; } @@ -94,8 +97,8 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable 3) - { - eventName = sm.GetConstantValue(args[3].Expression, _cancellationToken).ToString(); - } - - var methodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken) as IMethodSymbol; - if (methodSymbol == null) - { - // semantic problem, just bail quietly - continue; - } - - var lm = new LoggerMethod - { - Name = method.Identifier.ToString(), - Level = level, - Message = message, - EventId = eventId, - EventName = eventName, - MessageHasTemplates = HasTemplates(message), - IsExtensionMethod = methodSymbol.IsExtensionMethod, - Modifiers = method.Modifiers.ToString(), - }; - - bool keep = true; // whether or not we want to keep the method definition or if it's got errors making it worth discarding instead - if (lm.Name.StartsWith("__", StringComparison.Ordinal)) - { - // can't have logging method names that start with __ since that can lead to conflicting symbol names - // because the generated symbols start with __ - Diag(DiagDescriptors.ErrorInvalidMethodName, method.Identifier.GetLocation()); - } - - if (sm.GetTypeInfo(method.ReturnType!).Type!.SpecialType != SpecialType.System_Void) - { - // logging methods must return void - Diag(DiagDescriptors.ErrorInvalidMethodReturnType, method.ReturnType.GetLocation()); - keep = false; - } - - if (method.Arity > 0) + if (args.Count > LoggerMessageAttrEventNameArg) { - // we don't currently support generic methods - Diag(DiagDescriptors.ErrorMethodIsGeneric, method.Identifier.GetLocation()); - keep = false; + eventName = sm.GetConstantValue(args[LoggerMessageAttrEventNameArg].Expression, _cancellationToken).ToString(); } - bool isStatic = false; - bool isPartial = false; - foreach (var mod in method.Modifiers) + var methodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken); + if (methodSymbol != null) { - switch (mod.Text) + var lm = new LoggerMethod { - case "partial": - isPartial = true; - break; + Name = method.Identifier.ToString(), + Level = level, + Message = message, + EventId = eventId, + EventName = eventName, + MessageHasTemplates = HasTemplates(message), + IsExtensionMethod = methodSymbol.IsExtensionMethod, + Modifiers = method.Modifiers.ToString(), + }; - case "static": - isStatic = true; - break; + bool keep = true; // whether or not we want to keep the method definition or if it's got errors making it worth discarding instead + if (lm.Name.StartsWith("_", StringComparison.Ordinal)) + { + // can't have logging method names that start with _ since that can lead to conflicting symbol names + // because the generated symbols start with _ + Diag(DiagDescriptors.ErrorInvalidMethodName, method.Identifier.GetLocation()); } - } - if (!isStatic) - { - Diag(DiagDescriptors.ErrorNotStaticMethod, method.GetLocation()); - keep = false; - } + if (sm.GetTypeInfo(method.ReturnType!).Type!.SpecialType != SpecialType.System_Void) + { + // logging methods must return void + Diag(DiagDescriptors.ErrorInvalidMethodReturnType, method.ReturnType.GetLocation()); + keep = false; + } - if (!isPartial) - { - Diag(DiagDescriptors.ErrorNotPartialMethod, method.GetLocation()); - keep = false; - } + if (method.Arity > 0) + { + // we don't currently support generic methods + Diag(DiagDescriptors.ErrorMethodIsGeneric, method.Identifier.GetLocation()); + keep = false; + } - // ensure there are no duplicate ids. - if (ids.Contains(lm.EventId)) - { - Diag(DiagDescriptors.ErrorEventIdReuse, args[0].GetLocation(), lm.EventId); - } - else - { - ids.Add(lm.EventId); - } + bool isStatic = false; + bool isPartial = false; + foreach (var mod in method.Modifiers) + { + switch (mod.Text) + { + case "partial": + isPartial = true; + break; - if (string.IsNullOrWhiteSpace(lm.Message)) - { - Diag(DiagDescriptors.ErrorInvalidMessage, ma.GetLocation(), method.Identifier.ToString()); - } + case "static": + isStatic = true; + break; + } + } - foreach (var p in method.ParameterList.Parameters) - { - var declaredType = sm.GetDeclaredSymbol(p); - if (declaredType == null) + if (!isStatic) { - // semantic problem, just bail quietly + Diag(DiagDescriptors.ErrorNotStaticMethod, method.GetLocation()); keep = false; - break; } - if (p.Type == null) + if (!isPartial) { - // semantic problem, just bail quietly + Diag(DiagDescriptors.ErrorNotPartialMethod, method.GetLocation()); keep = false; - break; } - var paramName = p.Identifier.ToString(); - if (string.IsNullOrWhiteSpace(paramName)) + // ensure there are no duplicate ids. + if (ids.Contains(lm.EventId)) { - // semantic problem, just bail quietly - keep = false; - break; + Diag(DiagDescriptors.ErrorEventIdReuse, args[0].GetLocation(), lm.EventId); } - - var pSymbol = sm.GetTypeInfo(p.Type).Type; - if (pSymbol == null || (pSymbol is IErrorTypeSymbol)) + else { - // semantic problem, just bail quietly - keep = false; - break; + _ = ids.Add(lm.EventId); } - var typeName = declaredType.ToDisplayString(); + if (string.IsNullOrWhiteSpace(lm.Message)) + { + Diag(DiagDescriptors.ErrorInvalidMessage, ma.GetLocation(), method.Identifier.ToString()); + } - // skip the ILogger parameter - if (p == method.ParameterList.Parameters[0]) + foreach (var p in method.ParameterList.Parameters) { - if (!IsBaseOrIdentity(pSymbol, loggerSymbol)) + var paramName = p.Identifier.ToString(); + if (string.IsNullOrWhiteSpace(paramName)) { - Diag(DiagDescriptors.ErrorFirstArgMustBeILogger, p.Identifier.GetLocation()); + // semantic problem, just bail quietly keep = false; + break; } - lm.LoggerType = typeName; - continue; - } + var paramSymbol = sm.GetTypeInfo(p.Type!).Type; + if (paramSymbol is IErrorTypeSymbol) + { + // semantic problem, just bail quietly + keep = false; + break; + } - var lp = new LoggerParameter - { - Name = paramName, - Type = typeName, - IsExceptionType = IsBaseOrIdentity(pSymbol, exSymbol), - }; + var declaredType = sm.GetDeclaredSymbol(p); + var typeName = declaredType!.ToDisplayString(); - lm.Parameters.Add(lp); + // skip the ILogger parameter + if (p == method.ParameterList.Parameters[0]) + { + if (!IsBaseOrIdentity(paramSymbol!, loggerSymbol)) + { + Diag(DiagDescriptors.ErrorFirstArgMustBeILogger, p.Identifier.GetLocation()); + keep = false; + } - if (lp.Name.StartsWith("__", StringComparison.Ordinal)) - { - // can't have logging method parameter names that start with __ since that can lead to conflicting symbol names - // because all generated symbols start with __ - Diag(DiagDescriptors.ErrorInvalidParameterName, p.Identifier.GetLocation()); - } - } + lm.LoggerType = typeName; + lm.LoggerName = paramName; + continue; + } - if (lc == null) - { - // determine the namespace the class is declared in, if any - var ns = classDef.Parent as NamespaceDeclarationSyntax; - if (ns == null) - { - if (classDef.Parent is not CompilationUnitSyntax) + var lp = new LoggerParameter { - // since this generator doesn't know how to generate a nested type... - Diag(DiagDescriptors.ErrorNestedType, classDef.Identifier.GetLocation()); - keep = false; + Name = paramName, + Type = typeName, + IsExceptionType = IsBaseOrIdentity(paramSymbol!, exceptionSymbol), + }; + + lm.Parameters.Add(lp); + + if (lp.Name.StartsWith("_", StringComparison.Ordinal)) + { + // can't have logging method parameter names that start with _ since that can lead to conflicting symbol names + // because all generated symbols start with _ + Diag(DiagDescriptors.ErrorInvalidParameterName, p.Identifier.GetLocation()); } } - else + + if (lc == null) { - nspace = ns.Name.ToString(); - for (; ; ) + // determine the namespace the class is declared in, if any + var ns = classDef.Parent as NamespaceDeclarationSyntax; + if (ns == null) { - ns = ns.Parent as NamespaceDeclarationSyntax; - if (ns == null) + if (classDef.Parent is not CompilationUnitSyntax) { - break; + // since this generator doesn't know how to generate a nested type... + Diag(DiagDescriptors.ErrorNestedType, classDef.Identifier.GetLocation()); + keep = false; } + } + else + { + nspace = ns.Name.ToString(); + while (true) + { + ns = ns.Parent as NamespaceDeclarationSyntax; + if (ns == null) + { + break; + } - nspace = $"{ns.Name}.{nspace}"; + nspace = $"{ns.Name}.{nspace}"; + } } } - } - if (keep) - { - if (lc == null) + if (keep) { - lc = new LoggerClass + if (lc == null) { - Namespace = nspace, - Name = classDef.Identifier.ToString(), - Constraints = classDef.ConstraintClauses.ToString(), - }; - - if (classDef.TypeParameterList != null) - { - lc.Name += classDef.TypeParameterList.ToString(); + lc = new LoggerClass + { + Namespace = nspace, + Name = classDef.Identifier.ToString() + classDef.TypeParameterList ?? string.Empty, + Constraints = classDef.ConstraintClauses.ToString(), + }; } - } - lc.Methods.Add(lm); + lc.Methods.Add(lm); + } } } } @@ -324,19 +306,8 @@ public IReadOnlyList GetLogClasses(IEnumerable - /// Does the string contain templates? + /// Checks if a string contain templates. /// private static bool HasTemplates(string message) { @@ -346,7 +317,22 @@ private static bool HasTemplates(string message) return false; } +#pragma warning disable S2692 // "IndexOf" checks should not be for positive numbers return message.IndexOf('}', start) > 0; +#pragma warning restore S2692 // "IndexOf" checks should not be for positive numbers + } + +#pragma warning disable SA1011 // Closing square brackets should be spaced correctly + private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs) +#pragma warning restore SA1011 // Closing square brackets should be spaced correctly + { + _reportDiagnostic(Diagnostic.Create(desc, location, messageArgs)); + } + + private bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest) + { + var conversion = _compilation.ClassifyConversion(source, dest); + return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit); } } @@ -360,7 +346,7 @@ internal class LoggerClass public string Namespace = string.Empty; public string Name = string.Empty; public string Constraints = string.Empty; - public List Methods = new(); + public List Methods = new (); } /// @@ -377,7 +363,8 @@ internal class LoggerMethod public bool IsExtensionMethod; public string Modifiers = string.Empty; public string LoggerType = string.Empty; - public List Parameters = new(); + public string LoggerName = string.Empty; + public List Parameters = new (); } /// diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs index 7c937f2e03064..b2a4ec4fb53f2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs @@ -1,23 +1,30 @@ -// © Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; +[assembly: System.Resources.NeutralResourcesLanguage("en-us")] +[assembly: InternalsVisibleTo("Microsoft.Extensions.Logging.Generators.Test")] + namespace Microsoft.Extensions.Logging.Generators { [Generator] public partial class LoggerMessageGenerator : ISourceGenerator { /// + [ExcludeFromCodeCoverage] public void Initialize(GeneratorInitializationContext context) { - context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); + context.RegisterForSyntaxNotifications(SyntaxReceiver.Create); } /// + [ExcludeFromCodeCoverage] public void Execute(GeneratorExecutionContext context) { var receiver = context.SyntaxReceiver as SyntaxReceiver; @@ -30,7 +37,7 @@ public void Execute(GeneratorExecutionContext context) var pascalCaseArguments = false; if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("PascalCaseArguments", out var value)) { - pascalCaseArguments = ((value.ToUpperInvariant() == "TRUE") || (value.ToUpperInvariant() == "YES")); + pascalCaseArguments = (value.ToUpperInvariant() == "TRUE") || (value.ToUpperInvariant() == "YES"); } var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); @@ -41,9 +48,15 @@ public void Execute(GeneratorExecutionContext context) context.AddSource(nameof(LoggerMessageGenerator), SourceText.From(result, Encoding.UTF8)); } + [ExcludeFromCodeCoverage] private sealed class SyntaxReceiver : ISyntaxReceiver { - public List ClassDeclarations { get; } = new(); + internal static ISyntaxReceiver Create() + { + return new SyntaxReceiver(); + } + + public List ClassDeclarations { get; } = new (); public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 2d08d7988f545..dbc2e4e0cf17d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -17,17 +17,7 @@ - - True - True - Resources.resx - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - + + diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs index 8616f759eb7cf..c75c1dc0c7748 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs @@ -115,7 +115,7 @@ internal static string ErrorInvalidMessageTitle { } /// - /// Looks up a localized string similar to Logging method names cannot start with __. + /// Looks up a localized string similar to Logging method names cannot start with _. /// internal static string ErrorInvalidMethodNameMessage { get { @@ -124,7 +124,7 @@ internal static string ErrorInvalidMethodNameMessage { } /// - /// Looks up a localized string similar to Logging method names cannot start with __. + /// Looks up a localized string similar to Logging method names cannot start with _. /// internal static string ErrorInvalidMethodNameTitle { get { @@ -151,7 +151,7 @@ internal static string ErrorInvalidMethodReturnTypeTitle { } /// - /// Looks up a localized string similar to Logging method parameter names cannot start with __. + /// Looks up a localized string similar to Logging method parameter names cannot start with _. /// internal static string ErrorInvalidParameterNameMessage { get { @@ -160,7 +160,7 @@ internal static string ErrorInvalidParameterNameMessage { } /// - /// Looks up a localized string similar to Logging method parameter names cannot start with __. + /// Looks up a localized string similar to Logging method parameter names cannot start with _. /// internal static string ErrorInvalidParameterNameTitle { get { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index 8689e356390cb..b433e9a2d66e2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -118,10 +118,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Logging method names cannot start with __ + Logging method names cannot start with _ - Logging method names cannot start with __ + Logging method names cannot start with _ Missing message for logging method @@ -130,10 +130,10 @@ Missing message for logging method {0} - Logging method parameter names cannot start with __ + Logging method parameter names cannot start with _ - Logging method parameter names cannot start with __ + Logging method parameter names cannot start with _ Logging class cannot be in nested types diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs index 3ed568900bf99..a015bc157800f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs @@ -1,20 +1,27 @@ // © Microsoft Corporation. All rights reserved. -using Microsoft.Extensions.Logging; using System; +using Microsoft.Extensions.Logging; -// NOTE: This source file serves two purposes. +// NOTE: This source file serves two purposes: // // #1. It is used to trigger the source generator during compilation of the test suite itself. The resulting generated code -// is then tested by GeneratedCodeTests.cs. This ensures the generated code works reliably. +// is then tested by LoggerMessageGeneratedCodeTests.cs. This ensures the generated code works reliably. // -// #2. It is loaded as a file from GeneratorEmitTests.cs, and then fed manually to the parser and then the generator. This is used -// strictly to calculate code coverage attained by the #1 case above. +// #2. It is loaded as a file from LoggerMessageGeneratorEmitterTests.cs, and then fed manually to the parser and then the generator. +// This is used strictly to calculate code coverage attained by the #1 case above. #pragma warning disable CA1801 // Review unused parameters +#pragma warning disable S1118 // Utility classes should not have public constructors +#pragma warning disable S3903 // Types should be defined in named namespaces +#pragma warning disable SA1202 // Elements should be ordered by access +#pragma warning disable SA1204 // Static elements should appear before instance elements +#pragma warning disable SA1207 // Protected should come before internal +#pragma warning disable SA1402 // File may only contain a single type +#pragma warning disable SA1403 // File may only contain a single namespace // Used to test use outside of a namespace -partial class NoNamespace +internal static partial class NoNamespace { [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] public static partial void CouldNotOpenSocket(ILogger logger, string hostName); @@ -23,7 +30,7 @@ partial class NoNamespace namespace Level1 { // used to test use inside a one-level namespace - partial class OneLevelNamespace + internal static partial class OneLevelNamespace { [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] public static partial void CouldNotOpenSocket(ILogger logger, string hostName); @@ -35,7 +42,7 @@ namespace Level1 namespace Level2 { // used to test use inside a two-level namespace - partial class TwoLevelNamespace + internal static partial class TwoLevelNamespace { [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] public static partial void CouldNotOpenSocket(ILogger logger, string hostName); @@ -43,11 +50,25 @@ partial class TwoLevelNamespace } } -namespace Microsoft.Extensions.Logging.Generators.Tests +namespace Microsoft.Extensions.Logging.Generators.Test { // test particular method signature variations are generated correctly - partial class SignatureTests where T : class + internal partial class SignatureTests + where T : class { + public static void Combo(ILogger logger, ILogger logger2) + { + M1(logger); + M2(logger); + M3(logger); + M4(logger2); + M5(logger, new[] { "A" }); + M6(logger); + M8(logger); + M9(logger); + M10(logger, null); + } + // normal public method [LoggerMessage(0, LogLevel.Critical, "Message1")] public static partial void M1(ILogger logger); @@ -87,23 +108,10 @@ partial class SignatureTests where T : class // nullable parameter [LoggerMessage(9, LogLevel.Critical, "Message10")] internal static partial void M10(ILogger logger, string? optional); - - public static void Combo(ILogger logger, ILogger logger2) - { - M1(logger); - M2(logger); - M3(logger); - M4(logger2); - M5(logger, new string[] { "A" }); - M6(logger); - M8(logger); - M9(logger); - M10(logger, null); - } } // test particular method signature variations are generated correctly - static partial class SignatureTests + internal static partial class SignatureTests { // extension method [LoggerMessage(10, LogLevel.Critical, "Message11")] @@ -115,7 +123,7 @@ public static void Combo(ILogger logger) } } - partial class ArgTestExtensions + internal static partial class ArgTestExtensions { [LoggerMessage(0, LogLevel.Error, "M1")] public static partial void Method1(ILogger logger); @@ -139,16 +147,20 @@ partial class ArgTestExtensions public static partial void Method7(ILogger logger, int p1, InvalidOperationException p2); [LoggerMessage(7, LogLevel.Error, "M8")] +#pragma warning disable S107 // Methods should not have too many parameters public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); +#pragma warning restore S107 // Methods should not have too many parameters [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] +#pragma warning disable S107 // Methods should not have too many parameters public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); +#pragma warning restore S107 // Methods should not have too many parameters [LoggerMessage(9, LogLevel.Error, "M10")] public static partial void Method10(ILogger logger, int p1); } - partial class ReadOnlyListExtensions + internal static partial class ReadOnlyListExtensions { [LoggerMessage(0, LogLevel.Error, "M0")] public static partial void M0(ILogger logger); @@ -172,10 +184,12 @@ partial class ReadOnlyListExtensions public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); [LoggerMessage(7, LogLevel.Error, "M7")] +#pragma warning disable S107 // Methods should not have too many parameters public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); +#pragma warning restore S107 // Methods should not have too many parameters } - partial class LevelTestExtensions + internal static partial class LevelTestExtensions { [LoggerMessage(0, LogLevel.Trace, "M0")] public static partial void M0(ILogger logger); @@ -202,7 +216,7 @@ partial class LevelTestExtensions public static partial void M7(ILogger logger); } - partial class EventNameTestExtensions + internal static partial class EventNameTestExtensions { [LoggerMessage(0, LogLevel.Trace, "M0", EventName = "CustomEventName")] public static partial void M0(ILogger logger); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index dcb42f75e30ad..677990eac0934 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using Xunit; -namespace Microsoft.Extensions.Logging.Generators.Tests +namespace Microsoft.Extensions.Logging.Generators.Test { public class LoggerMessageGeneratedCodeTests { @@ -34,7 +34,6 @@ public void BasicTests() Assert.Null(logger.LastException); Assert.Equal("Could not open socket to `microsoft.com`", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); - } [Fact] @@ -147,31 +146,9 @@ public void ReadOnlyListTest() logger.Reset(); ReadOnlyListExtensions.M7(logger, 0, 1, 2, 3, 4, 5, 6); TestCollection(7, logger); - } - - private static void TestCollection(int expected, MockLogger logger) - { - var rol = (logger.LastState as IReadOnlyList>)!; - Assert.NotNull(rol); - Assert.Equal(expected, rol.Count); - for (int i = 0; i < expected; i++) - { - var kvp = new KeyValuePair($"p{i}", i); - Assert.Equal(kvp, rol[i]); - } - - int count = 0; - foreach (var actual in rol) - { - var kvp = new KeyValuePair($"p{count}", count); - Assert.Equal(kvp, actual); - count++; - } - - Assert.Equal(expected, count); - - Assert.Throws(() => _ = rol[expected]); + // TestCollection is doing all the testing for us + Assert.True(true); } [Fact] @@ -249,5 +226,32 @@ public void EventNameTests() Assert.Equal(1, logger.CallCount); Assert.Equal("CustomEventName", logger.LastEventId.Name); } + + private static void TestCollection(int expected, MockLogger logger) + { +#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly + var rol = (logger.LastState as IReadOnlyList>)!; +#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly + Assert.NotNull(rol); + + Assert.Equal(expected, rol.Count); + for (int i = 0; i < expected; i++) + { + var kvp = new KeyValuePair($"p{i}", i); + Assert.Equal(kvp, rol[i]); + } + + int count = 0; + foreach (var actual in rol) + { + var kvp = new KeyValuePair($"p{count}", count); + Assert.Equal(kvp, actual); + count++; + } + + Assert.Equal(expected, count); + + Assert.Throws(() => _ = rol[expected]); + } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs similarity index 70% rename from src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitTests.cs rename to src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 193c089634f96..604a6ffb10328 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -1,5 +1,6 @@ // © Microsoft Corporation. All rights reserved. +using System; using System.IO; using System.Linq; using System.Threading; @@ -9,9 +10,9 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Xunit; -namespace Microsoft.Extensions.Logging.Generators.Tests +namespace Microsoft.Extensions.Logging.Generators.Test { - public class LoggerMessageGeneratorEmitTests + public class LoggerMessageGeneratorEmitterTests { [Fact] public async Task TestEmitter() @@ -23,8 +24,13 @@ public async Task TestEmitter() .WithDocument("Definitions.cs", testSourceCode); await proj.CommitChanges("CS8795").ConfigureAwait(false); +#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly var comp = (await proj.GetCompilationAsync().ConfigureAwait(false))!; +#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly + // This tests exists strictly to calculate the code coverage + // attained by processing Definitions.cs. The functionality of the + // resulting code is tested via LoggerMessageGeneratedCodeTests.cs for (int i = 0; i < 2; i++) { var p = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Parser(comp, d => { }, CancellationToken.None); @@ -37,8 +43,7 @@ public async Task TestEmitter() var generatedSource = e.Emit(lc, CancellationToken.None); Assert.True(!string.IsNullOrEmpty(generatedSource)); - generatedSource = e.Emit(lc, new CancellationToken(true)); - Assert.True(string.IsNullOrEmpty(generatedSource)); + Assert.Throws(() => _ = e.Emit(lc, new CancellationToken(true))); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 6b4deca103aa5..8dd0691904900 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -10,7 +10,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Xunit; -namespace Microsoft.Extensions.Logging.Generators.Tests +namespace Microsoft.Extensions.Logging.Generators.Test { public class LoggerMessageGeneratorParserTests { @@ -106,7 +106,7 @@ partial class C Assert.Empty(lc); Assert.Single(d); Assert.Equal("LG0004", d[0].Id); - + (lc, d) = TryParser(@" partial class C { @@ -378,7 +378,7 @@ partial class C [System.Obsolete(""Foo"")] static partial void M1(ILogger logger); } - "); + ", checkDiags: false); Assert.Empty(lc); Assert.Empty(d); @@ -442,19 +442,19 @@ private static (IReadOnlyList, IReadOnlyList var text = code; if (wrap) { - var nsStart = "namespace Test {"; - var nsEnd = "}"; + var nspaceStart = "namespace Test {"; + var nspaceEnd = "}"; if (!inNamespace) { - nsStart = ""; - nsEnd = ""; + nspaceStart = ""; + nspaceEnd = ""; } text = $@" - {nsStart} + {nspaceStart} using Microsoft.Extensions.Logging; {code} - {nsEnd} + {nspaceEnd} {RoslynTestUtils.LoggingBoilerplate} "; } @@ -462,7 +462,12 @@ private static (IReadOnlyList, IReadOnlyList var refs = Array.Empty(); if (includeReferences) { - refs = new[] { MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location) }; +#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly + refs = new[] + { + MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location), + }; +#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly } var compilation = CSharpCompilation.Create( @@ -478,9 +483,13 @@ private static (IReadOnlyList, IReadOnlyList } var results = new List(); - var p = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Parser(compilation, (d) => { - results.Add(d); - }, cancellationToken); + var p = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Parser( + compilation, + (d) => + { + results.Add(d); + }, + cancellationToken); var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Test.csproj similarity index 100% rename from src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj rename to src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Test.csproj diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs index b5383e0ac30a4..b5d045e52fb8d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -1,13 +1,14 @@ // © Microsoft Corporation. All rights reserved. using System; +using Microsoft.Extensions.Logging; -namespace Microsoft.Extensions.Logging.Generators.Tests +namespace Microsoft.Extensions.Logging.Generators.Test { /// /// A logger which captures the last log state logged to it. /// - class MockLogger : ILogger + internal class MockLogger : ILogger { public LogLevel LastLogLevel { get; private set; } public EventId LastEventId { get; private set; } @@ -20,10 +21,11 @@ class MockLogger : ILogger /// /// Dummy disposable type, for use with BeginScope /// - class Disposable : IDisposable + private class Disposable : IDisposable { public void Dispose() { + // nothing } } @@ -55,7 +57,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except public void Reset() { LastLogLevel = (LogLevel)(-1); - LastEventId = new EventId(); + LastEventId = default; LastState = null; LastException = null; LastFormattedString = null; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs index 3995c4b348d61..11609871a9788 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs @@ -10,9 +10,9 @@ using Microsoft.CodeAnalysis.Text; using Xunit; -namespace Microsoft.Extensions.Logging.Generators.Tests +namespace Microsoft.Extensions.Logging.Generators.Test { - static class RoslynTestUtils + internal static class RoslynTestUtils { public static Project CreateTestProject() { @@ -20,7 +20,9 @@ public static Project CreateTestProject() return new AdhocWorkspace() .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create())) .AddProject("Test", "test.dll", "C#") +#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly .WithMetadataReferences(new[] { MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location) }) +#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithNullableContextOptions(NullableContextOptions.Enable)); #pragma warning restore CA2000 // Dispose objects before losing scope } @@ -66,6 +68,19 @@ public static Project WithDocument(this Project proj, string name, string text) } public const string LoggingBoilerplate = @" + namespace Microsoft.Extensions.Logging + { + [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] + public sealed class LoggerMessageAttribute : System.Attribute + { + public LoggerMessageAttribute(int eventId, Microsoft.Extensions.Logging.LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); + public int EventId { get; } + public string? EventName { get; set; } + public Microsoft.Extensions.Logging.LogLevel Level { get; } + public string Message { get; } + } + } + namespace Microsoft.Extensions.Logging { using System; @@ -81,16 +96,6 @@ public enum LogLevel None, } - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] - public sealed class LoggerMessageAttribute : System.Attribute - { - public LoggerMessageAttribute(int eventId, LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); - public int EventId { get; set; } - public string? EventName { get; set; } - public LogLevel Level { get; set; } - public string Message { get; set; } - } - public interface ILogger { } @@ -166,6 +171,7 @@ public static TextSpan MakeSpan(string text, int spanNum) { throw new ArgumentOutOfRangeException(nameof(spanNum)); } + start += 6; int end = text.IndexOf($"/*-{spanNum}*/", StringComparison.Ordinal); @@ -173,6 +179,7 @@ public static TextSpan MakeSpan(string text, int spanNum) { throw new ArgumentOutOfRangeException(nameof(spanNum)); } + end -= 1; return new TextSpan(start, end - start); From e8db38a65dd4f50c11305cb285020e01b2a21186 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 7 Mar 2021 19:55:11 -0800 Subject: [PATCH 062/121] A few improvements - The code generator reports a few more errors. - Substantially improve the little Roslyn testing framework ignored created for this. - A few analysis-driven improvements. --- .../gen/DiagDescriptors.cs | 48 ++ .../gen/LoggerMessageGenerator.Emitter.cs | 34 +- .../gen/LoggerMessageGenerator.Parser.cs | 258 +++++++++-- .../gen/LoggerMessageGenerator.cs | 5 +- .../gen/Resources.Designer.cs | 110 ++++- .../gen/Resources.resx | 90 ++-- .../Definitions.cs | 38 +- .../LoggerMessageGeneratedCodeTests.cs | 14 +- .../LoggerMessageGeneratorEmitterTests.cs | 75 ++-- .../LoggerMessageGeneratorParserTests.cs | 419 ++++++++---------- .../MockLogger.cs | 4 +- .../RoslynTestUtils.cs | 307 +++++++++---- 12 files changed, 930 insertions(+), 472 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs index 2327d8c6256c8..750aacbf94415 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs @@ -93,5 +93,53 @@ internal static class DiagDescriptors category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static DiagnosticDescriptor RedundantQualifierInMessage { get; } = new ( + id: "LG0011", + title: Resources.RedundantQualifierInMessageTitle, + messageFormat: Resources.RedundantQualifierInMessageMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public static DiagnosticDescriptor PassingDateTime { get; } = new ( + id: "LG0012", + title: Resources.PassingDateTimeTitle, + messageFormat: Resources.PassingDateTimeMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public static DiagnosticDescriptor DontMentionExceptionInMessage { get; } = new ( + id: "LG0013", + title: Resources.DontMentionExceptionInMessageTitle, + messageFormat: Resources.DontMentionExceptionInMessageMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public static DiagnosticDescriptor TemplateHasNoCorrespondingArgument { get; } = new ( + id: "LG0014", + title: Resources.TemplateHasNoCorrespondingArgumentTitle, + messageFormat: Resources.TemplateHasNoCorrespondingArgumentMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ArgumentHasNoCorrespondingTemplate { get; } = new ( + id: "LG0015", + title: Resources.ArgumentHasNoCorrespondingTemplateTitle, + messageFormat: Resources.ArgumentHasNoCorrespondingTemplateMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorMethodHasBody { get; } = new ( + id: "LG0016", + title: Resources.ErrorMethodHasBodyTitle, + messageFormat: Resources.ErrorMethodHasBodyMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 677dcc28c2793..d5af43f71d33d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -152,7 +152,7 @@ private string GenFormatFunc(LoggerMethod lm) } return $@" - [System.Runtime.CompilerServices.CompilerGenerated] + [global::System.Runtime.CompilerServices.CompilerGenerated] private static readonly global::System.Func<{typeName}, global::System.Exception?, string> _format{lm.Name} = (_holder, _) => {{ {sb} @@ -176,7 +176,7 @@ private string GenNameArray(LoggerMethod lm) var sb = GetStringBuilder(); try { - _ = sb.Append($"\n [System.Runtime.CompilerServices.CompilerGenerated]\n"); + _ = sb.Append("\n [global::System.Runtime.CompilerServices.CompilerGenerated]\n"); _ = sb.Append($" private static readonly string[] _names{lm.Name} = new[] {{ "); foreach (var p in lm.Parameters) { @@ -219,7 +219,7 @@ private string GenLogMethod(LoggerMethod lm) string exceptionArg = "null"; foreach (var p in lm.Parameters) { - if (p.IsExceptionType) + if (p.IsException) { exceptionArg = p.Name; break; @@ -238,7 +238,7 @@ private string GenLogMethod(LoggerMethod lm) #pragma warning disable S103 // Lines should not be too long return $@" - [System.Runtime.CompilerServices.CompilerGenerated] + [global::System.Runtime.CompilerServices.CompilerGenerated] {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} {lm.LoggerName}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) {{ if ({lm.LoggerName}.IsEnabled({level})) @@ -343,22 +343,22 @@ private string GenHolder(LoggerMethod lm, string formatFunc) private string NormalizeArgumentName(string name) { - if (!_pascalCaseArguments || char.IsUpper(name, 0)) + if (_pascalCaseArguments) { - return name; + var sb = GetStringBuilder(); + try + { + _ = sb.Append(char.ToUpperInvariant(name[0])); + _ = sb.Append(name, 1, name.Length - 1); + name = sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } } - var sb = GetStringBuilder(); - try - { - _ = sb.Append(char.ToUpperInvariant(name[0])). - Append(name, 1, name.Length - 1); - return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } + return name; } // our own cheezy object pool since we can't use the .NET core version (since this code runs in legacy .NET framework) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 2f44d70cf162e..cecbe1b9a3a74 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -46,17 +45,24 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable? templates = null; + if (!string.IsNullOrWhiteSpace(message)) + { + templates = ExtractTemplateArgs(message); + } + var lm = new LoggerMethod { Name = method.Identifier.ToString(), @@ -126,13 +131,13 @@ public IReadOnlyList GetLogClasses(IEnumerable 0, IsExtensionMethod = methodSymbol.IsExtensionMethod, Modifiers = method.Modifiers.ToString(), }; bool keep = true; // whether or not we want to keep the method definition or if it's got errors making it worth discarding instead - if (lm.Name.StartsWith("_", StringComparison.Ordinal)) + if (lm.Name[0] == '_') { // can't have logging method names that start with _ since that can lead to conflicting symbol names // because the generated symbols start with _ @@ -181,6 +186,12 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable - /// Checks if a string contain templates. + /// Finds the template arguments contained in the message string. /// - private static bool HasTemplates(string message) + private static List ExtractTemplateArgs(string message) { - int start = message.IndexOf('{'); - if (start < 0) + var args = new List(); + var scanIndex = 0; + var endIndex = message.Length; + + while (scanIndex < endIndex) { - return false; + var openBraceIndex = FindBraceIndex(message, '{', scanIndex, endIndex); + var closeBraceIndex = FindBraceIndex(message, '}', openBraceIndex, endIndex); + + if (closeBraceIndex == endIndex) + { + scanIndex = endIndex; + } + else + { + // Format item syntax : { index[,alignment][ :formatString] }. + var formatDelimiterIndex = FindIndexOfAny(message, _formatDelimiters, openBraceIndex, closeBraceIndex); + + args.Add(message.Substring(openBraceIndex + 1, formatDelimiterIndex - openBraceIndex - 1)); + scanIndex = closeBraceIndex + 1; + } } -#pragma warning disable S2692 // "IndexOf" checks should not be for positive numbers - return message.IndexOf('}', start) > 0; -#pragma warning restore S2692 // "IndexOf" checks should not be for positive numbers + return args; } -#pragma warning disable SA1011 // Closing square brackets should be spaced correctly - private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs) -#pragma warning restore SA1011 // Closing square brackets should be spaced correctly + private static int FindBraceIndex(string message, char brace, int startIndex, int endIndex) { - _reportDiagnostic(Diagnostic.Create(desc, location, messageArgs)); + // Example: {{prefix{{{Argument}}}suffix}}. + var braceIndex = endIndex; + var scanIndex = startIndex; + var braceOccurrenceCount = 0; + + while (scanIndex < endIndex) + { + if (braceOccurrenceCount > 0 && message[scanIndex] != brace) + { +#pragma warning disable S109 // Magic numbers should not be used + if (braceOccurrenceCount % 2 == 0) +#pragma warning restore S109 // Magic numbers should not be used + { + // Even number of '{' or '}' found. Proceed search with next occurrence of '{' or '}'. + braceOccurrenceCount = 0; + braceIndex = endIndex; + } + else + { + // An unescaped '{' or '}' found. + break; + } + } + else if (message[scanIndex] == brace) + { + if (brace == '}') + { + if (braceOccurrenceCount == 0) + { + // For '}' pick the first occurrence. + braceIndex = scanIndex; + } + } + else + { + // For '{' pick the last occurrence. + braceIndex = scanIndex; + } + + braceOccurrenceCount++; + } + + scanIndex++; + } + + return braceIndex; } - private bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest) + private static int FindIndexOfAny(string message, char[] chars, int startIndex, int endIndex) { - var conversion = _compilation.ClassifyConversion(source, dest); - return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit); + var findIndex = message.IndexOfAny(chars, startIndex, endIndex - startIndex); + return findIndex == -1 ? endIndex : findIndex; } } @@ -343,10 +505,10 @@ private bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest) /// internal class LoggerClass { + public readonly List Methods = new (); public string Namespace = string.Empty; public string Name = string.Empty; public string Constraints = string.Empty; - public List Methods = new (); } /// @@ -354,6 +516,7 @@ internal class LoggerClass /// internal class LoggerMethod { + public readonly List Parameters = new (); public string Name = string.Empty; public string Message = string.Empty; public int Level; @@ -364,7 +527,6 @@ internal class LoggerMethod public string Modifiers = string.Empty; public string LoggerType = string.Empty; public string LoggerName = string.Empty; - public List Parameters = new (); } /// @@ -374,7 +536,7 @@ internal class LoggerParameter { public string Name = string.Empty; public string Type = string.Empty; - public bool IsExceptionType; + public bool IsException; } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs index b2a4ec4fb53f2..920769473a627 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs @@ -16,14 +16,12 @@ namespace Microsoft.Extensions.Logging.Generators [Generator] public partial class LoggerMessageGenerator : ISourceGenerator { - /// [ExcludeFromCodeCoverage] public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(SyntaxReceiver.Create); } - /// [ExcludeFromCodeCoverage] public void Execute(GeneratorExecutionContext context) { @@ -60,8 +58,7 @@ internal static ISyntaxReceiver Create() public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { - var classSyntax = syntaxNode as ClassDeclarationSyntax; - if (classSyntax != null) + if (syntaxNode is ClassDeclarationSyntax classSyntax) { ClassDeclarations.Add(classSyntax); } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs index c75c1dc0c7748..6f43dfcd7ab6e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.Extensions.Logging.Generators { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -60,6 +60,42 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to Argument {0} is not referenced from the logging message. + /// + internal static string ArgumentHasNoCorrespondingTemplateMessage { + get { + return ResourceManager.GetString("ArgumentHasNoCorrespondingTemplateMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Argument is not referenced from the logging message. + /// + internal static string ArgumentHasNoCorrespondingTemplateTitle { + get { + return ResourceManager.GetString("ArgumentHasNoCorrespondingTemplateTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care. + /// + internal static string DontMentionExceptionInMessageMessage { + get { + return ResourceManager.GetString("DontMentionExceptionInMessageMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't include exception parameters as templates in the logging message. + /// + internal static string DontMentionExceptionInMessageTitle { + get { + return ResourceManager.GetString("DontMentionExceptionInMessageTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Multiple logging messages are using event id {0}. /// @@ -168,6 +204,24 @@ internal static string ErrorInvalidParameterNameTitle { } } + /// + /// Looks up a localized string similar to Logging methods cannot have a body. + /// + internal static string ErrorMethodHasBodyMessage { + get { + return ResourceManager.GetString("ErrorMethodHasBodyMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging methods cannot have a body. + /// + internal static string ErrorMethodHasBodyTitle { + get { + return ResourceManager.GetString("ErrorMethodHasBodyTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Logging methods cannot be generic. /// @@ -257,5 +311,59 @@ internal static string ErrorNotStaticMethodTitle { return ResourceManager.GetString("ErrorNotStaticMethodTitle", resourceCulture); } } + + /// + /// Looks up a localized string similar to No need to supply a DateTime value as argument since the logging infrastructure inserts one implicitly. + /// + internal static string PassingDateTimeMessage { + get { + return ResourceManager.GetString("PassingDateTimeMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No need to supply a DateTime value as argument since the logging infrastructure inserts one implicitly. + /// + internal static string PassingDateTimeTitle { + get { + return ResourceManager.GetString("PassingDateTimeTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.. + /// + internal static string RedundantQualifierInMessageMessage { + get { + return ResourceManager.GetString("RedundantQualifierInMessageMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Redundant qualifier in logging message. + /// + internal static string RedundantQualifierInMessageTitle { + get { + return ResourceManager.GetString("RedundantQualifierInMessageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Template {0} is not provided as argument to the logging method. + /// + internal static string TemplateHasNoCorrespondingArgumentMessage { + get { + return ResourceManager.GetString("TemplateHasNoCorrespondingArgumentMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging template has no corresponding method argument. + /// + internal static string TemplateHasNoCorrespondingArgumentTitle { + get { + return ResourceManager.GetString("TemplateHasNoCorrespondingArgumentTitle", resourceCulture); + } + } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index b433e9a2d66e2..5b84d456bd2e6 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -1,17 +1,17 @@  - @@ -183,4 +183,40 @@ Logging methods cannot be generic + + Don't include a template for {0} in the logging message since it is implicitly taken care + + + Don't include exception parameters as templates in the logging message + + + No need to supply a DateTime value as argument since the logging infrastructure inserts one implicitly + + + No need to supply a DateTime value as argument since the logging infrastructure inserts one implicitly + + + Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. + + + Redundant qualifier in logging message + + + Argument {0} is not referenced from the logging message + + + Argument is not referenced from the logging message + + + Template {0} is not provided as argument to the logging method + + + Logging template has no corresponding method argument + + + Logging methods cannot have a body + + + Logging methods cannot have a body + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs index a015bc157800f..06e2c8e493e8b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs @@ -1,4 +1,4 @@ -// © Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. using System; using Microsoft.Extensions.Logging; @@ -86,15 +86,15 @@ public static void Combo(ILogger logger, ILogger logger2) private static partial void M4(ILogger logger); // random type method parameter - [LoggerMessage(4, LogLevel.Critical, "Message5")] + [LoggerMessage(4, LogLevel.Critical, "Message5 {items}")] private static partial void M5(ILogger logger, System.Collections.IEnumerable items); - // linefeeds and quotes in the message string + // line feeds and quotes in the message string [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] private static partial void M6(ILogger logger); // generic parameter - [LoggerMessage(6, LogLevel.Critical, "Message7\n\"\r")] + [LoggerMessage(6, LogLevel.Critical, "Message7 {p1}\n\"\r")] private static partial void M7(ILogger logger, T p1); // normal public method @@ -103,10 +103,10 @@ public static void Combo(ILogger logger, ILogger logger2) // internal method [LoggerMessage(8, LogLevel.Critical, "Message9")] - internal protected static partial void M9(ILogger logger); + protected internal static partial void M9(ILogger logger); // nullable parameter - [LoggerMessage(9, LogLevel.Critical, "Message10")] + [LoggerMessage(9, LogLevel.Critical, "Message10 {optional}")] internal static partial void M10(ILogger logger, string? optional); } @@ -128,7 +128,7 @@ internal static partial class ArgTestExtensions [LoggerMessage(0, LogLevel.Error, "M1")] public static partial void Method1(ILogger logger); - [LoggerMessage(1, LogLevel.Error, "M2")] + [LoggerMessage(1, LogLevel.Error, "M2 {p1}")] public static partial void Method2(ILogger logger, string p1); [LoggerMessage(2, LogLevel.Error, "M3 {p1} {p2}")] @@ -137,16 +137,16 @@ internal static partial class ArgTestExtensions [LoggerMessage(3, LogLevel.Error, "M4")] public static partial void Method4(ILogger logger, InvalidOperationException p1); - [LoggerMessage(4, LogLevel.Error, "M5")] + [LoggerMessage(4, LogLevel.Error, "M5 {p2}")] public static partial void Method5(ILogger logger, InvalidOperationException p1, InvalidOperationException p2); - [LoggerMessage(5, LogLevel.Error, "M6")] + [LoggerMessage(5, LogLevel.Error, "M6 {p2}")] public static partial void Method6(ILogger logger, InvalidOperationException p1, int p2); - [LoggerMessage(6, LogLevel.Error, "M7")] + [LoggerMessage(6, LogLevel.Error, "M7 {p1}")] public static partial void Method7(ILogger logger, int p1, InvalidOperationException p2); - [LoggerMessage(7, LogLevel.Error, "M8")] + [LoggerMessage(7, LogLevel.Error, "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] #pragma warning disable S107 // Methods should not have too many parameters public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); #pragma warning restore S107 // Methods should not have too many parameters @@ -156,7 +156,7 @@ internal static partial class ArgTestExtensions public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); #pragma warning restore S107 // Methods should not have too many parameters - [LoggerMessage(9, LogLevel.Error, "M10")] + [LoggerMessage(9, LogLevel.Error, "M10{p1}")] public static partial void Method10(ILogger logger, int p1); } @@ -165,25 +165,25 @@ internal static partial class ReadOnlyListExtensions [LoggerMessage(0, LogLevel.Error, "M0")] public static partial void M0(ILogger logger); - [LoggerMessage(1, LogLevel.Error, "M1")] + [LoggerMessage(1, LogLevel.Error, "M1{p0}")] public static partial void M1(ILogger logger, int p0); - [LoggerMessage(2, LogLevel.Error, "M2")] + [LoggerMessage(2, LogLevel.Error, "M2{p0}{p1}")] public static partial void M2(ILogger logger, int p0, int p1); - [LoggerMessage(3, LogLevel.Error, "M3")] + [LoggerMessage(3, LogLevel.Error, "M3{p0}{p1}{p2}")] public static partial void M3(ILogger logger, int p0, int p1, int p2); - [LoggerMessage(4, LogLevel.Error, "M4")] + [LoggerMessage(4, LogLevel.Error, "M4{p0}{p1}{p2}{p3}")] public static partial void M4(ILogger logger, int p0, int p1, int p2, int p3); - [LoggerMessage(5, LogLevel.Error, "M5")] + [LoggerMessage(5, LogLevel.Error, "M5{p0}{p1}{p2}{p3}{p4}")] public static partial void M5(ILogger logger, int p0, int p1, int p2, int p3, int p4); - [LoggerMessage(6, LogLevel.Error, "M6")] + [LoggerMessage(6, LogLevel.Error, "M6{p0}{p1}{p2}{p3}{p4}{p5}")] public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); - [LoggerMessage(7, LogLevel.Error, "M7")] + [LoggerMessage(7, LogLevel.Error, "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] #pragma warning disable S107 // Methods should not have too many parameters public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); #pragma warning restore S107 // Methods should not have too many parameters diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 677990eac0934..2caa82c3efb40 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -1,4 +1,4 @@ -// © Microsoft Corporation. All rights reserved. +// � Microsoft Corporation. All rights reserved. using System; using System.Collections.Generic; @@ -61,7 +61,7 @@ public void ArgTest() logger.Reset(); ArgTestExtensions.Method2(logger, "arg1"); Assert.Null(logger.LastException); - Assert.Equal("M2", logger.LastFormattedString); + Assert.Equal("M2 arg1", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); @@ -79,24 +79,24 @@ public void ArgTest() logger.Reset(); ArgTestExtensions.Method5(logger, new InvalidOperationException("A"), new InvalidOperationException("B")); Assert.Equal("A", logger.LastException!.Message); - Assert.Equal("M5", logger.LastFormattedString); + Assert.Equal("M5 System.InvalidOperationException: B", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); ArgTestExtensions.Method6(logger, new InvalidOperationException("A"), 2); Assert.Equal("A", logger.LastException!.Message); - Assert.Equal("M6", logger.LastFormattedString); + Assert.Equal("M6 2", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); ArgTestExtensions.Method7(logger, 1, new InvalidOperationException("B")); Assert.Equal("B", logger.LastException!.Message); - Assert.Equal("M7", logger.LastFormattedString); + Assert.Equal("M7 1", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); ArgTestExtensions.Method8(logger, 1, 2, 3, 4, 5, 6, 7); - Assert.Equal("M8", logger.LastFormattedString); + Assert.Equal("M81234567", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); logger.Reset(); @@ -106,7 +106,7 @@ public void ArgTest() logger.Reset(); ArgTestExtensions.Method10(logger, 1); - Assert.Equal("M10", logger.LastFormattedString); + Assert.Equal("M101", logger.LastFormattedString); Assert.Equal(1, logger.CallCount); } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 604a6ffb10328..154ceb4a0776d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -1,50 +1,71 @@ -// © Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. using System; +using System.Collections.Immutable; using System.IO; -using System.Linq; -using System.Threading; +using System.Reflection; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Xunit; namespace Microsoft.Extensions.Logging.Generators.Test { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "")] public class LoggerMessageGeneratorEmitterTests { - [Fact] - public async Task TestEmitter() + private class Options : AnalyzerConfigOptions { - var testSourceCode = File.ReadAllText(@"..\..\..\Definitions.cs"); + private readonly string _response; - var proj = RoslynTestUtils.CreateTestProject() - .WithLoggingBoilerplate() - .WithDocument("Definitions.cs", testSourceCode); + public Options(string response) + { + _response = response; + } - await proj.CommitChanges("CS8795").ConfigureAwait(false); -#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly - var comp = (await proj.GetCompilationAsync().ConfigureAwait(false))!; -#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly + public override bool TryGetValue(string key, out string value) + { + value = _response; + return _response.Length > 0; + } + } - // This tests exists strictly to calculate the code coverage + private class OptionsProvider : AnalyzerConfigOptionsProvider + { + private readonly string _response; + + public OptionsProvider(string response) + { + _response = response; + } + + public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) => throw new NotImplementedException(); + public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) => throw new NotImplementedException(); + public override AnalyzerConfigOptions GlobalOptions => new Options(_response); + } + + [Theory] + [InlineData("")] + [InlineData("TRUE")] + [InlineData("FALSE")] + public async Task TestEmitter(string response) + { + // This test exists strictly to calculate the code coverage // attained by processing Definitions.cs. The functionality of the // resulting code is tested via LoggerMessageGeneratedCodeTests.cs - for (int i = 0; i < 2; i++) - { - var p = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Parser(comp, d => { }, CancellationToken.None); - var e = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Emitter(i == 0); - var allNodes = comp.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); - var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); - var lc = p.GetLogClasses(allClasses); + var testSourceCode = await File.ReadAllTextAsync(@"..\..\..\Definitions.cs"); - var generatedSource = e.Emit(lc, CancellationToken.None); - Assert.True(!string.IsNullOrEmpty(generatedSource)); + var (d, r) = await RoslynTestUtils.RunGenerator( + new LoggerMessageGenerator(), +#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly + new[] { Assembly.GetAssembly(typeof(ILogger))!, Assembly.GetAssembly(typeof(LoggerMessageAttribute))! }, +#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly + new[] { testSourceCode }, + optionsProvider: new OptionsProvider(response)).ConfigureAwait(false); - Assert.Throws(() => _ = e.Emit(lc, new CancellationToken(true))); - } + Assert.Empty(d); + Assert.Single(r); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 8dd0691904900..f144c955e1b10 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -1,23 +1,22 @@ -// © Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Xunit; namespace Microsoft.Extensions.Logging.Generators.Test { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "")] public class LoggerMessageGeneratorParserTests { [Fact] - public void InvalidMethodName() + public async Task InvalidMethodName() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -25,15 +24,33 @@ partial class C } "); - Assert.Single(lc); Assert.Single(d); Assert.Equal("LG0000", d[0].Id); } [Fact] - public void InvalidMessage() + public async Task InvalidMethodBody() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" + partial class C + { + static partial void M1(ILogger logger); + + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger) + { + } + } + "); + + Assert.Single(d); + Assert.Equal("LG0016", d[0].Id); + } + + [Fact] + public async Task InvalidMessage() + { + var d = await RunGenerator(@" partial class C { [LoggerMessage(0, LogLevel.Debug, """")] @@ -41,31 +58,104 @@ partial class C } "); - Assert.Single(lc); Assert.Single(d); Assert.Equal("LG0001", d[0].Id); } [Fact] - public void InvalidParameterName() + public async Task MissingTemplate() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] + [LoggerMessage(0, LogLevel.Debug, ""This is a message without foo"")] + static partial void M1(ILogger logger, string foo); + } + "); + + Assert.Single(d); + Assert.Equal("LG0015", d[0].Id); + } + + [Fact] + public async Task MissingArgument() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""{foo}"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Single(d); + Assert.Equal("LG0014", d[0].Id); + } + + [Fact] + public async Task NeedlessQualifierInMessage() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, LogLevel.Information, ""INFO: this is an informative message"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Single(d); + Assert.Equal("LG0011", d[0].Id); + } + + [Fact] + public async Task NeedlessExceptionInMessage() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1 {ex} {ex2}"")] + static partial void M1(ILogger logger, System.Exception ex, System.Exception ex2); + } + "); + + Assert.Single(d); + Assert.Equal("LG0013", d[0].Id); + } + + [Fact] + public async Task InvalidParameterName() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1 {__foo}"")] static partial void M1(ILogger logger, string __foo); } "); - Assert.Single(lc); Assert.Single(d); Assert.Equal("LG0002", d[0].Id); } [Fact] - public void NestedType() + public async Task DateTimeAsParameterType() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1 {timeStamp}"")] + static partial void M1(ILogger logger, System.DateTime timeStamp); + } + "); + + Assert.Single(d); + Assert.Equal("LG0012", d[0].Id); + } + + [Fact] + public async Task NestedType() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { public partial class Nested @@ -76,15 +166,14 @@ public partial class Nested } "); - Assert.Empty(lc); Assert.Single(d); Assert.Equal("LG0003", d[0].Id); } [Fact] - public void RequiredTypes() + public async Task MissingExceptionType() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" namespace System { public class Object @@ -101,23 +190,58 @@ namespace Microsoft.Extensions.Logging partial class C { } - ", false, includeReferences: false); + ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Empty(lc); Assert.Single(d); Assert.Equal("LG0004", d[0].Id); + } - (lc, d) = TryParser(@" + [Fact] + public async Task MissingDateTimeType() + { + var d = await RunGenerator(@" + namespace System + { + public class Object + { + } + + public class Void + { + } + + public class Exception + { + } + } + namespace Microsoft.Extensions.Logging + { + } partial class C { } - ", false); + ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Empty(lc); Assert.Single(d); Assert.Equal("LG0004", d[0].Id); + } - (lc, d) = TryParser(@" + [Fact] + public async Task MissingLoggerMessageAttributeType() + { + var d = await RunGenerator(@" + partial class C + { + } + ", false, includeLoggingReferences: false); + + Assert.Empty(d); + } + + [Fact] + public async Task MissingILoggerType() + { + var d = await RunGenerator(@" namespace Microsoft.Extensions.Logging { public sealed class LoggerMessageAttribute : System.Attribute {} @@ -125,17 +249,15 @@ public sealed class LoggerMessageAttribute : System.Attribute {} partial class C { } - ", false); + ", false, includeLoggingReferences: false); - Assert.Empty(lc); - Assert.Single(d); - Assert.Equal("LG0004", d[0].Id); + Assert.Empty(d); } [Fact] - public void EventIdReuse() + public async Task EventIdReuse() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -146,15 +268,14 @@ partial class C } "); - Assert.Single(lc); Assert.Single(d); Assert.Equal("LG0005", d[0].Id); } [Fact] - public void MethodReturnType() + public async Task MethodReturnType() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -164,31 +285,29 @@ partial class C } "); - Assert.Empty(lc); Assert.Single(d); Assert.Equal("LG0006", d[0].Id); } [Fact] - public void FirstArgILogger() + public async Task FirstArgILogger() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] + [LoggerMessage(0, LogLevel.Debug, ""M1 {p1} {logger}"")] static partial void M1(int p1, ILogger logger); } "); - Assert.Empty(lc); Assert.Single(d); Assert.Equal("LG0007", d[0].Id); } [Fact] - public void NotStatic() + public async Task NotStatic() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -196,15 +315,14 @@ partial class C } "); - Assert.Empty(lc); Assert.Single(d); Assert.Equal("LG0008", d[0].Id); } [Fact] - public void NotPartial() + public async Task NotPartial() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -212,15 +330,15 @@ static void M1(ILogger logger) {} } "); - Assert.Empty(lc); - Assert.Single(d); + Assert.Equal(2, d.Count); Assert.Equal("LG0009", d[0].Id); + Assert.Equal("LG0016", d[1].Id); } [Fact] - public void MethodGeneric() + public async Task MethodGeneric() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] @@ -228,181 +346,59 @@ partial class C } "); - Assert.Empty(lc); Assert.Single(d); Assert.Equal("LG0010", d[0].Id); } [Fact] - public void Templates() + public async Task Templates() { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" partial class C { [LoggerMessage(1, LogLevel.Debug, ""M1"")] - static partial void M1(ILogger logger, string arg1, string arg2); + static partial void M1(ILogger logger); [LoggerMessage(2, LogLevel.Debug, ""M2 {arg1} {arg2}"")] static partial void M2(ILogger logger, string arg1, string arg2); [LoggerMessage(3, LogLevel.Debug, ""M3 {arg1"")] - static partial void M3(ILogger logger, string arg1); + static partial void M3(ILogger logger); [LoggerMessage(4, LogLevel.Debug, ""M4 arg1}"")] - static partial void M4(ILogger logger, string arg1); + static partial void M4(ILogger logger); [LoggerMessage(5, LogLevel.Debug, ""M5 {"")] - static partial void M5(ILogger logger, string arg1); - + static partial void M5(ILogger logger); + [LoggerMessage(6, LogLevel.Debug, ""}M6 "")] - static partial void M6(ILogger logger, string arg1); + static partial void M6(ILogger logger); [LoggerMessage(7, LogLevel.Debug, ""M7 {{arg1}}"")] - static partial void M7(ILogger logger, string arg1); - } - "); - - Assert.Single(lc); - Assert.False(lc[0].Methods[0].MessageHasTemplates); - Assert.True(lc[0].Methods[1].MessageHasTemplates); - Assert.False(lc[0].Methods[2].MessageHasTemplates); - Assert.False(lc[0].Methods[3].MessageHasTemplates); - Assert.False(lc[0].Methods[4].MessageHasTemplates); - Assert.False(lc[0].Methods[5].MessageHasTemplates); - Assert.True(lc[0].Methods[6].MessageHasTemplates); - Assert.Empty(d); - } - - [Fact] - public void Namespace() - { - var (lc, d) = TryParser(@" - namespace Foo - { - partial class C - { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - static partial void M1(ILogger logger); - } - } - "); - - Assert.Single(lc); - Assert.Equal("Test.Foo", lc[0].Namespace); - Assert.Equal("C", lc[0].Name); - Assert.Empty(d); - - (lc, d) = TryParser(@" - partial class C - { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - static partial void M1(ILogger logger); - } - "); - - Assert.Single(lc); - Assert.Equal("Test", lc[0].Namespace); - Assert.Equal("C", lc[0].Name); - Assert.Empty(d); - - (lc, d) = TryParser(@" - partial class C - { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - static partial void M1(ILogger logger); - } - ", true, false); - - Assert.Single(lc); - Assert.Equal(string.Empty, lc[0].Namespace); - Assert.Equal("C", lc[0].Name); - Assert.Empty(d); - } - - [Fact] - public void Generic() - { - var (lc, d) = TryParser(@" - partial class C - { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - static partial void M1(ILogger logger); - } - "); - - Assert.Single(lc); - Assert.Equal("Test", lc[0].Namespace); - Assert.Equal("C", lc[0].Name); - Assert.Empty(d); - } - - [Fact] - public void EventName() - { - var (lc, d) = TryParser(@" - partial class C - { - [LoggerMessage(0, LogLevel.Debug, ""M1"", EventName = ""MyEvent"")] - static partial void M1(ILogger logger); + static partial void M7(ILogger logger); } "); - Assert.Single(lc); - Assert.Equal("Test", lc[0].Namespace); - Assert.Equal("C", lc[0].Name); - Assert.Equal("MyEvent", lc[0].Methods[0].EventName); Assert.Empty(d); } [Fact] - public void Cancellation() + public async Task Cancellation() { - var (lc, d) = TryParser(@" + await Assert.ThrowsAsync(async () => + _ = await RunGenerator(@" partial class C { [LoggerMessage(0, LogLevel.Debug, ""M1"")] static partial void M1(ILogger logger); } - ", cancellationToken: new CancellationToken(true)); - - Assert.Empty(lc); - Assert.Empty(d); + ", cancellationToken: new CancellationToken(true))); } [Fact] - public void RandomAttribute() + public async Task SourceErrors() { - var (lc, d) = TryParser(@" - partial class C - { - [System.Obsolete(""Foo"")] - static partial void M1(ILogger logger); - } - ", checkDiags: false); - - Assert.Empty(lc); - Assert.Empty(d); - } - - [Fact] - public void ExtensionMethod() - { - var (lc, d) = TryParser(@" - static partial class C - { - [LoggerMessage(0, LogLevel.Debug, ""Hello"")] - static partial void M1(this ILogger logger); - } - "); - - Assert.True(lc[0].Methods[0].IsExtensionMethod); - Assert.Empty(d); - } - - [Fact] - public void SourceErrors() - { - var (lc, d) = TryParser(@" + var d = await RunGenerator(@" static partial class C { // bogus argument type @@ -425,18 +421,17 @@ static partial class C [LoggerMessage(4, "", ""Hello"")] int M5; } - ", checkDiags: false); + "); - Assert.Empty(lc); Assert.Empty(d); // should fail quietly on broken code } - private static (IReadOnlyList, IReadOnlyList) TryParser( + private static async Task> RunGenerator( string code, bool wrap = true, bool inNamespace = true, - bool includeReferences = true, - bool checkDiags = true, + bool includeBaseReferences = true, + bool includeLoggingReferences = true, CancellationToken cancellationToken = default) { var text = code; @@ -455,47 +450,27 @@ private static (IReadOnlyList, IReadOnlyList using Microsoft.Extensions.Logging; {code} {nspaceEnd} - {RoslynTestUtils.LoggingBoilerplate} "; } - var refs = Array.Empty(); - if (includeReferences) +#pragma warning disable SA1011 // Closing square brackets should be spaced correctly + Assembly[]? refs = null; +#pragma warning restore SA1011 // Closing square brackets should be spaced correctly + if (includeLoggingReferences) { #pragma warning disable SA1009 // Closing parenthesis should be spaced correctly - refs = new[] - { - MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location), - }; + refs = new[] { Assembly.GetAssembly(typeof(ILogger))!, Assembly.GetAssembly(typeof(LoggerMessageAttribute))! }; #pragma warning restore SA1009 // Closing parenthesis should be spaced correctly } - var compilation = CSharpCompilation.Create( - "example.dll", - new[] { CSharpSyntaxTree.ParseText(text, cancellationToken: CancellationToken.None) }, - refs) - .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithNullableContextOptions(NullableContextOptions.Enable)); - - if (checkDiags) - { - // make sure we have valid syntax - Assert.Empty(compilation.GetDiagnostics(CancellationToken.None)); - } - - var results = new List(); - var p = new Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator.Parser( - compilation, - (d) => - { - results.Add(d); - }, - cancellationToken); - - var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes()); - var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType(); - var lc = p.GetLogClasses(allClasses); + var (d, r) = await RoslynTestUtils.RunGenerator( + new LoggerMessageGenerator(), + refs, + new[] { text }, + includeBaseReferences: includeBaseReferences, + cancellationToken: cancellationToken).ConfigureAwait(false); - return (lc, results); + return d; } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs index b5d045e52fb8d..6f9bcc368e6a2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -1,4 +1,4 @@ -// © Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. using System; using Microsoft.Extensions.Logging; @@ -19,7 +19,7 @@ internal class MockLogger : ILogger public int CallCount { get; private set; } /// - /// Dummy disposable type, for use with BeginScope + /// Dummy disposable type, for use with BeginScope. /// private class Disposable : IDisposable { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs index 11609871a9788..7edad95ebc5ca 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs @@ -1,12 +1,18 @@ -// © Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; +using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -14,24 +20,43 @@ namespace Microsoft.Extensions.Logging.Generators.Test { internal static class RoslynTestUtils { - public static Project CreateTestProject() + /// + /// Creates a canonical Roslyn project for testing. + /// + /// Assembly references to include in the project. + /// Whether to include references to the BCL assemblies. + public static Project CreateTestProject(IEnumerable? references, bool includeBaseReferences = true) { +#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly + var corelib = Assembly.GetAssembly(typeof(object))!.Location; + var runtimeDir = Path.GetDirectoryName(corelib)!; +#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly + + var refs = new List(); + if (includeBaseReferences) + { + refs.Add(MetadataReference.CreateFromFile(corelib)); + refs.Add(MetadataReference.CreateFromFile(Path.Combine(runtimeDir, "netstandard.dll"))); + refs.Add(MetadataReference.CreateFromFile(Path.Combine(runtimeDir, "System.Runtime.dll"))); + } + + if (references != null) + { + foreach (var r in references) + { + refs.Add(MetadataReference.CreateFromFile(r.Location)); + } + } + #pragma warning disable CA2000 // Dispose objects before losing scope return new AdhocWorkspace() .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create())) .AddProject("Test", "test.dll", "C#") -#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly - .WithMetadataReferences(new[] { MetadataReference.CreateFromFile(Assembly.GetAssembly(typeof(System.Exception))!.Location) }) -#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly + .WithMetadataReferences(refs) .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithNullableContextOptions(NullableContextOptions.Enable)); #pragma warning restore CA2000 // Dispose objects before losing scope } - public static void Dispose(this Project proj) - { - proj.Solution.Workspace.Dispose(); - } - public static Task CommitChanges(this Project proj, params string[] ignorables) { Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution)); @@ -47,15 +72,7 @@ public static async Task AssertNoDiagnostic(this Project proj, params string[] i foreach (var d in sm!.GetDiagnostics()) { - bool ignore = false; - foreach (var ig in ignorables) - { - if (d.Id == ig) - { - ignore = true; - break; - } - } + bool ignore = ignorables.Any(ig => d.Id == ig); Assert.True(ignore, d.ToString()); } @@ -67,122 +84,216 @@ public static Project WithDocument(this Project proj, string name, string text) return proj.AddDocument(name, text).Project; } - public const string LoggingBoilerplate = @" - namespace Microsoft.Extensions.Logging + public static Document FindDocument(this Project proj, string name) + { + foreach (var doc in proj.Documents) { - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)] - public sealed class LoggerMessageAttribute : System.Attribute + if (doc.Name == name) { - public LoggerMessageAttribute(int eventId, Microsoft.Extensions.Logging.LogLevel level, string message) => (EventId, Level, Message) = (eventId, level, message); - public int EventId { get; } - public string? EventName { get; set; } - public Microsoft.Extensions.Logging.LogLevel Level { get; } - public string Message { get; } + return doc; } } - namespace Microsoft.Extensions.Logging + throw new FileNotFoundException(name); + } + + /// + /// Looks for /*N+*/ and /*-N*/ markers in a string and creates a TextSpan containing the enclosed text. + /// + public static TextSpan MakeSpan(string text, int spanNum) + { + int start = text.IndexOf($"/*{spanNum}+*/", StringComparison.Ordinal); + if (start < 0) + { + throw new ArgumentOutOfRangeException(nameof(spanNum)); + } + + start += 6; + + int end = text.IndexOf($"/*-{spanNum}*/", StringComparison.Ordinal); + if (end < 0) + { + throw new ArgumentOutOfRangeException(nameof(spanNum)); + } + + end -= 1; + + return new TextSpan(start, end - start); + } + + /// + /// Runs a Roslyn generator over a set of source files. + /// + public static async Task<(ImmutableArray, ImmutableArray)> RunGenerator( + ISourceGenerator generator, + IEnumerable? references, + IEnumerable sources, + AnalyzerConfigOptionsProvider? optionsProvider = null, + bool includeBaseReferences = true, + CancellationToken cancellationToken = default) + { + var proj = CreateTestProject(references, includeBaseReferences); + + var count = 0; + foreach (var s in sources) { - using System; + proj = proj.WithDocument($"src-{count++}.cs", s); + } + + Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution)); + + var comp = await proj!.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false); + + var cgd = CSharpGeneratorDriver.Create(new[] { generator }, optionsProvider: optionsProvider); + var gd = cgd.RunGenerators(comp!, cancellationToken); + + var r = gd.GetRunResult(); + return (r.Results[0].Diagnostics, r.Results[0].GeneratedSources); + } - public enum LogLevel + /// + /// Runs a Roslyn analyzer over a set of source files. + /// + public static async Task> RunAnalyzer( + DiagnosticAnalyzer analyzer, + IEnumerable references, + IEnumerable sources) + { + var proj = CreateTestProject(references); + + var count = 0; + foreach (var s in sources) + { + proj = proj.WithDocument($"src-{count++}.cs", s); + } + + await proj.CommitChanges().ConfigureAwait(false); + + var analyzers = ImmutableArray.Create(analyzer); + + var comp = await proj!.GetCompilationAsync().ConfigureAwait(false); + return await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false); + } + + /// + /// Runs a Roslyn analyzer and fixer. + /// + public static async Task> RunAnalyzerAndFixer( + DiagnosticAnalyzer analyzer, + CodeFixProvider fixer, + IEnumerable references, + IEnumerable sources, + IEnumerable? sourceNames = null, + string? defaultNamespace = null, + string? extraFile = null) + { + var proj = CreateTestProject(references); + + var count = 0; + if (sourceNames != null) + { + var l = sourceNames.ToList(); + foreach (var s in sources) + { + proj = proj.WithDocument(l[count++], s); + } + } + else + { + foreach (var s in sources) { - Trace, - Debug, - Information, - Warning, - Error, - Critical, - None, + proj = proj.WithDocument($"src-{count++}.cs", s); } + } - public interface ILogger + if (defaultNamespace != null) + { + proj = proj.WithDefaultNamespace(defaultNamespace); + } + + await proj.CommitChanges().ConfigureAwait(false); + + var analyzers = ImmutableArray.Create(analyzer); + + while (true) + { + var comp = await proj!.GetCompilationAsync().ConfigureAwait(false); + var diags = await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false); + if (diags.IsEmpty) { + // no more diagnostics reported by the analyzers + break; } - public interface ILogger : ILogger + var actions = new List(); + foreach (var d in diags) { + var doc = proj.GetDocument(d.Location.SourceTree); + + var context = new CodeFixContext(doc!, d, (action, _) => actions.Add(action), CancellationToken.None); + await fixer.RegisterCodeFixesAsync(context).ConfigureAwait(false); } - public struct EventId + if (actions.Count == 0) { - public EventId(int id, string name) {} + // nothing to fix + break; } - public static class LoggerExtensions + var operations = await actions[0].GetOperationsAsync(CancellationToken.None).ConfigureAwait(false); + var solution = operations.OfType().Single().ChangedSolution; + var changedProj = solution.GetProject(proj.Id); + if (changedProj != proj) { - public static void Log(this ILogger logger, LogLevel logLevel, Exception exception, string message, params object[] args){} - public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, string message, params object[] args){} - public static void Log(this ILogger logger, LogLevel logLevel, string message, params object[] args){} - public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, Exception exception, string message, params object[] args){} - public static void LogCritical(this ILogger logger, string message, params object[] args){} - public static void LogCritical(this ILogger logger, Exception exception, string message, params object[] args){} - public static void LogCritical(this ILogger logger, EventId eventId, string message, params object[] args){} - public static void LogCritical(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} - public static void LogDebug(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} - public static void LogDebug(this ILogger logger, EventId eventId, string message, params object[] args){} - public static void LogDebug(this ILogger logger, Exception exception, string message, params object[] args){} - public static void LogDebug(this ILogger logger, string message, params object[] args){} - public static void LogError(this ILogger logger, string message, params object[] args){} - public static void LogError(this ILogger logger, Exception exception, string message, params object[] args){} - public static void LogError(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} - public static void LogError(this ILogger logger, EventId eventId, string message, params object[] args){} - public static void LogInformation(this ILogger logger, EventId eventId, string message, params object[] args){} - public static void LogInformation(this ILogger logger, Exception exception, string message, params object[] args){} - public static void LogInformation(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} - public static void LogInformation(this ILogger logger, string message, params object[] args){} - public static void LogTrace(this ILogger logger, string message, params object[] args){} - public static void LogTrace(this ILogger logger, Exception exception, string message, params object[] args){} - public static void LogTrace(this ILogger logger, EventId eventId, string message, params object[] args){} - public static void LogTrace(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} - public static void LogWarning(this ILogger logger, EventId eventId, string message, params object[] args){} - public static void LogWarning(this ILogger logger, EventId eventId, Exception exception, string message, params object[] args){} - public static void LogWarning(this ILogger logger, string message, params object[] args){} - public static void LogWarning(this ILogger logger, Exception exception, string message, params object[] args){} + proj = await RecreateProjectDocumentsAsync(changedProj!).ConfigureAwait(false); } } - "; - public static Project WithLoggingBoilerplate(this Project proj) - { - return proj.AddDocument("boilerplate.cs", LoggingBoilerplate).Project; - } + var results = new List(); - public static Document FindDocument(this Project proj, string name) - { - foreach (var doc in proj.Documents) + if (sourceNames != null) { - if (doc.Name == name) + var l = sourceNames.ToList(); + for (int i = 0; i < count; i++) { - return doc; + var s = await proj.FindDocument(l[i]).GetTextAsync().ConfigureAwait(false); + results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal)); + } + } + else + { + for (int i = 0; i < count; i++) + { + var s = await proj.FindDocument($"src-{i}.cs").GetTextAsync().ConfigureAwait(false); + results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal)); } } - throw new FileNotFoundException(name); - } - - /// - /// Looks for /*N+*/ and /*-N*/ markers in a string and creates a TextSpan containing the enclosed text. - /// - public static TextSpan MakeSpan(string text, int spanNum) - { - int start = text.IndexOf($"/*{spanNum}+*/", StringComparison.Ordinal); - if (start < 0) + if (extraFile != null) { - throw new ArgumentOutOfRangeException(nameof(spanNum)); + var s = await proj.FindDocument(extraFile).GetTextAsync().ConfigureAwait(false); + results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal)); } - start += 6; + return results; + } - int end = text.IndexOf($"/*-{spanNum}*/", StringComparison.Ordinal); - if (end < 0) + private static async Task RecreateProjectDocumentsAsync(Project project) + { + foreach (var documentId in project.DocumentIds) { - throw new ArgumentOutOfRangeException(nameof(spanNum)); + var document = project.GetDocument(documentId); + document = await RecreateDocumentAsync(document!).ConfigureAwait(false); + project = document.Project; } - end -= 1; + return project; + } - return new TextSpan(start, end - start); + private static async Task RecreateDocumentAsync(Document document) + { + var newText = await document.GetTextAsync().ConfigureAwait(false); + return document.WithText(SourceText.From(newText.ToString(), newText.Encoding, newText.ChecksumAlgorithm)); } } } From 5e3d27997a69d59475fb4fd0329a14879b1d62dd Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 14 Mar 2021 08:52:03 -0700 Subject: [PATCH 063/121] Variety of improvements. - Added support for dynamic log levels. Rather than putting the log level in the [LoggerMessage] attribute, you can put it as an argument to the logging method. This allows dynamic log levels. - Made the LogStateHolder types classes instead of structs. This dedicates one allocation in order to save multiple boxings of the struct within the ILogger implementation. In addition, the generated code ends up faster because it doesn't have to copy around a potentially large struct. - Fix logic that was parsing the [LoggerMessage] attribute values. it wasn't dealing with using name: syntax to initialize the values of an attribute. --- .../gen/DiagDescriptors.cs | 16 ++ .../gen/LoggerMessageGenerator.Emitter.cs | 40 ++-- .../gen/LoggerMessageGenerator.Parser.cs | 173 +++++++++++++----- .../gen/Resources.Designer.cs | 36 ++++ .../gen/Resources.resx | 66 ++++--- .../Definitions.cs | 7 +- .../LoggerMessageGeneratorParserTests.cs | 52 +++++- 7 files changed, 309 insertions(+), 81 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs index 750aacbf94415..9adb6f3194ed1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs @@ -141,5 +141,21 @@ internal static class DiagDescriptors category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static DiagnosticDescriptor ErrorMissingLogLevel { get; } = new ( + id: "LG0017", + title: Resources.ErrorMissingLogLevelTitle, + messageFormat: Resources.ErrorMissingLogLevelMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor DontMentionLogLevelInMessage { get; } = new ( + id: "LG0018", + title: Resources.DontMentionLogLevelInMessageTitle, + messageFormat: Resources.DontMentionLogLevelInMessageMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index d5af43f71d33d..6ecb1a75a6ad7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -194,17 +194,35 @@ private string GenNameArray(LoggerMethod lm) private string GenLogMethod(LoggerMethod lm) { - string level = lm.Level switch + string level = string.Empty; + + if (lm.Level == null) { - 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", - 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", - 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", - 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", - 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", - 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", - 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", - _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", - }; + foreach (var p in lm.Parameters) + { + if (p.IsLogLevel) + { + level = p.Name; + break; + } + } + } + else + { +#pragma warning disable S109 // Magic numbers should not be used + level = lm.Level switch + { + 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", + 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", + 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", + 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", + 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", + 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", + 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", + _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", + }; +#pragma warning restore S109 // Magic numbers should not be used + } string eventName; if (string.IsNullOrWhiteSpace(lm.EventName)) @@ -282,7 +300,7 @@ private string GenHolder(LoggerMethod lm, string formatFunc) { if (lm.Parameters.Count == 0) { - return $"default(global::{StateHolderNamespace}.LogStateHolder)"; + return $"new global::{StateHolderNamespace}.LogStateHolder({formatFunc})"; } if (lm.Parameters.Count == 1) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index cecbe1b9a3a74..667d3ff015a1b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -31,10 +31,6 @@ public Parser(Compilation compilation, Action reportDiagnostic, Canc public IReadOnlyList GetLogClasses(IEnumerable classes) { const string LoggerMessageAttribute = "Microsoft.Extensions.Logging.LoggerMessageAttribute"; - const int LoggerMessageAttrEventIdArg = 0; - const int LoggerMessageAttrLevelArg = 1; - const int LoggerMessageAttrMessageArg = 2; - const int LoggerMessageAttrEventNameArg = 3; var results = new List(); @@ -66,7 +62,14 @@ public IReadOnlyList GetLogClasses(IEnumerable(); + var logLevelSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LogLevel"); + if (logLevelSymbol == null) + { + // nothing to do if this type isn't available + return results; + } + + var ids = new HashSet(); // we enumerate by syntax tree, to minimize the need to instantiate semantic models (since they're expensive) foreach (var group in classes.GroupBy(x => x.SyntaxTree)) @@ -96,24 +99,14 @@ public IReadOnlyList GetLogClasses(IEnumerable LoggerMessageAttrEventNameArg) - { - eventName = sm.GetConstantValue(args[LoggerMessageAttrEventNameArg].Expression, _cancellationToken).ToString(); - } + var (eventId, level, message, eventName) = ExtractAttributeValues(ma.ArgumentList!, sm); var methodSymbol = sm.GetDeclaredSymbol(method, _cancellationToken); if (methodSymbol != null) @@ -136,7 +129,7 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable 0) { // we don't currently support generic methods Diag(DiagDescriptors.ErrorMethodIsGeneric, method.Identifier.GetLocation()); - keep = false; + keepMethod = false; } bool isStatic = false; @@ -177,25 +170,25 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable Parameters = new (); public string Name = string.Empty; public string Message = string.Empty; - public int Level; - public string EventId = string.Empty; + public int? Level; + public int EventId; public string EventName = string.Empty; public bool MessageHasTemplates; public bool IsExtensionMethod; @@ -537,6 +627,7 @@ internal class LoggerParameter public string Name = string.Empty; public string Type = string.Empty; public bool IsException; + public bool IsLogLevel; } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs index 6f43dfcd7ab6e..b69ff47509908 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs @@ -96,6 +96,24 @@ internal static string DontMentionExceptionInMessageTitle { } } + /// + /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. + /// + internal static string DontMentionLogLevelInMessageMessage { + get { + return ResourceManager.GetString("DontMentionLogLevelInMessageMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't include log level parameters as templates in the logging message.. + /// + internal static string DontMentionLogLevelInMessageTitle { + get { + return ResourceManager.GetString("DontMentionLogLevelInMessageTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Multiple logging messages are using event id {0}. /// @@ -240,6 +258,24 @@ internal static string ErrorMethodIsGenericTitle { } } + /// + /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. + /// + internal static string ErrorMissingLogLevelMessage { + get { + return ResourceManager.GetString("ErrorMissingLogLevelMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. + /// + internal static string ErrorMissingLogLevelTitle { + get { + return ResourceManager.GetString("ErrorMissingLogLevelTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Could not find definition for type {0}. /// diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index 5b84d456bd2e6..f5abfbcdb3451 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -1,17 +1,17 @@  - @@ -219,4 +219,16 @@ Logging methods cannot have a body + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method + + + Don't include a template for {0} in the logging message since it is implicitly taken care of + + + Don't include log level parameters as templates in the logging message. + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs index 06e2c8e493e8b..2f26cb1b8c1dc 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs @@ -67,6 +67,7 @@ public static void Combo(ILogger logger, ILogger logger2) M8(logger); M9(logger); M10(logger, null); + M11(logger, "A", LogLevel.Debug, "B"); } // normal public method @@ -108,13 +109,17 @@ public static void Combo(ILogger logger, ILogger logger2) // nullable parameter [LoggerMessage(9, LogLevel.Critical, "Message10 {optional}")] internal static partial void M10(ILogger logger, string? optional); + + // dynamic log level + [LoggerMessage(10, "Message11 {p1} {p2}")] + internal static partial void M11(ILogger logger, string p1, LogLevel level, string p2); } // test particular method signature variations are generated correctly internal static partial class SignatureTests { // extension method - [LoggerMessage(10, LogLevel.Critical, "Message11")] + [LoggerMessage(eventId: 10, level: LogLevel.Critical, message: "Message11")] internal static partial void M11(this ILogger logger); public static void Combo(ILogger logger) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index f144c955e1b10..8dd0f9ef7aeef 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -28,6 +28,21 @@ partial class C Assert.Equal("LG0000", d[0].Id); } + [Fact] + public async Task MissingLogLevel() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, ""M1"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Single(d); + Assert.Equal("LG0017", d[0].Id); + } + [Fact] public async Task InvalidMethodBody() { @@ -122,6 +137,21 @@ partial class C Assert.Equal("LG0013", d[0].Id); } + [Fact] + public async Task NeedlessLogLevelInMessage() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, ""M1 {l1} {l2}"")] + static partial void M1(ILogger logger, LogLevel l1, LogLevel l2); + } + "); + + Assert.Single(d); + Assert.Equal("LG0018", d[0].Id); + } + [Fact] public async Task InvalidParameterName() { @@ -242,10 +272,30 @@ partial class C public async Task MissingILoggerType() { var d = await RunGenerator(@" - namespace Microsoft.Extensions.Logging + namespace Microsoft.R9.Extensions.Logging + { + public sealed class LoggerMessageAttribute : System.Attribute {} + } + partial class C + { + } + ", false, includeLoggingReferences: false); + + Assert.Empty(d); + } + + [Fact] + public async Task MissingLogLevelType() + { + var d = await RunGenerator(@" + namespace Microsoft.R9.Extensions.Logging { public sealed class LoggerMessageAttribute : System.Attribute {} } + namespace Microsoft.Extensions.Logging + { + public interface ILogger {} + } partial class C { } From 00cac9b251be022e056497121eb69cc7666618b8 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 14 Mar 2021 20:04:26 -0700 Subject: [PATCH 064/121] Various improvements. - The ILogger parameter to logging methods no longer needs to be the first argument in the logging method. As long as it is one of the arguments, everything will work. - Support specifying the [LoggerMessage] attribute without an actual message string. When this happens, an auto-generated message string is produced which outputs the logging method parameters in JSON format. - Fixed bug where the LogStateHolder object was being populated with exception and log level objects when those should have been stripped away - Specifying an argument to a logging method, but not supplying a template for it in the log message is now treated as a warning instead of an error. --- .../gen/DiagDescriptors.cs | 20 +- .../gen/LoggerMessageGenerator.Emitter.cs | 147 ++++++++--- .../gen/LoggerMessageGenerator.Parser.cs | 143 +++++------ .../gen/Resources.Designer.cs | 70 +++--- .../gen/Resources.resx | 20 +- .../Definitions.cs | 229 ------------------ .../LoggerMessageGeneratedCodeTests.cs | 92 ++++++- .../LoggerMessageGeneratorEmitterTests.cs | 8 +- .../LoggerMessageGeneratorParserTests.cs | 44 ++-- .../TestClasses/ArgTestExtensions.cs | 45 ++++ .../TestClasses/CollectionTestExtensions.cs | 40 +++ .../TestClasses/EventNameTestExtensions.cs | 12 + .../TestClasses/ExceptionTestExtensions.cs | 17 ++ .../TestClasses/LevelTestExtensions.cs | 39 +++ .../TestClasses/MessageTestExtensions.cs | 21 ++ .../TestClasses/MiscTestExtensions.cs | 42 ++++ .../TestClasses/README.md | 7 + .../TestClasses/SignatureTestExtensions.cs | 82 +++++++ 18 files changed, 650 insertions(+), 428 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/README.md create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs index 9adb6f3194ed1..e1b087b9bfcd9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs @@ -14,10 +14,10 @@ internal static class DiagDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorInvalidMessage { get; } = new ( + public static DiagnosticDescriptor DontMentionLogLevelInMessage { get; } = new ( id: "LG0001", - title: Resources.ErrorInvalidMessageTitle, - messageFormat: Resources.ErrorInvalidMessageMessage, + title: Resources.DontMentionLogLevelInMessageTitle, + messageFormat: Resources.DontMentionLogLevelInMessageMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -62,10 +62,10 @@ internal static class DiagDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorFirstArgMustBeILogger { get; } = new ( + public static DiagnosticDescriptor ErrorMissingLogger { get; } = new ( id: "LG0007", - title: Resources.ErrorFirstArgMustBeILoggerTitle, - messageFormat: Resources.ErrorFirstArgMustBeILoggerMessage, + title: Resources.ErrorMissingLoggerTitle, + messageFormat: Resources.ErrorMissingLoggerMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -131,7 +131,7 @@ internal static class DiagDescriptors title: Resources.ArgumentHasNoCorrespondingTemplateTitle, messageFormat: Resources.ArgumentHasNoCorrespondingTemplateMessage, category: "LoggingGenerator", - DiagnosticSeverity.Error, + DiagnosticSeverity.Warning, isEnabledByDefault: true); public static DiagnosticDescriptor ErrorMethodHasBody { get; } = new ( @@ -150,10 +150,10 @@ internal static class DiagDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor DontMentionLogLevelInMessage { get; } = new ( + public static DiagnosticDescriptor DontMentionLoggerInMessage { get; } = new ( id: "LG0018", - title: Resources.DontMentionLogLevelInMessageTitle, - messageFormat: Resources.DontMentionLogLevelInMessageMessage, + title: Resources.DontMentionLoggerInMessageTitle, + messageFormat: Resources.DontMentionLoggerInMessageMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 6ecb1a75a6ad7..f8430cbc97aa5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -59,6 +59,11 @@ private string GenType(LoggerClass lc) var sb = GetStringBuilder(); try { + foreach (var lm in lc.Methods) + { + AutoGenerateMessage(lm); + } + foreach (var lm in lc.Methods) { _ = sb.Append(GenNameArray(lm)); @@ -102,7 +107,7 @@ partial class {lc.Name} {lc.Constraints} private string GenFormatFunc(LoggerMethod lm) { - if (!lm.MessageHasTemplates) + if (lm.Templates.Count == 0) { return string.Empty; } @@ -111,28 +116,21 @@ private string GenFormatFunc(LoggerMethod lm) try { string typeName; - if (lm.Parameters.Count == 1) + if (lm.RegularParameters.Count == 1) { - typeName = $"global::{StateHolderNamespace}.LogStateHolder<{lm.Parameters[0].Type}>"; - _ = sb.Append($" var {lm.Parameters[0].Name} = _holder.Value;\n"); + typeName = $"global::{StateHolderNamespace}.LogStateHolder<{lm.RegularParameters[0].Type}>"; } - else if (lm.Parameters.Count > MaxStateHolderArity) + else if (lm.RegularParameters.Count > MaxStateHolderArity) { typeName = $"global::{StateHolderNamespace}.LogStateHolderN"; - - var index = 0; - foreach (var p in lm.Parameters) - { - _ = sb.Append($" var {p.Name} = _holder[{index++}].Value;\n"); - } } else { _ = sb.Append($"global::{StateHolderNamespace}.LogStateHolder<"); - foreach (var p in lm.Parameters) + foreach (var p in lm.RegularParameters) { - if (p != lm.Parameters[0]) + if (p != lm.RegularParameters[0]) { _ = sb.Append(", "); } @@ -142,12 +140,35 @@ private string GenFormatFunc(LoggerMethod lm) _ = sb.Append('>'); typeName = sb.ToString(); - _ = sb.Clear(); - var index = 1; - foreach (var p in lm.Parameters) + } + + foreach (var t in lm.Templates) + { + if (lm.RegularParameters.Count == 1) { - _ = sb.Append($" var {p.Name} = _holder.Value{index++};\n"); + _ = sb.Append($" var {t} = _holder.Value;\n"); + } + else + { + int index = 0; + foreach (var p in lm.RegularParameters) + { + if (p.Name == t) + { + break; + } + index++; + } + + if (lm.RegularParameters.Count > MaxStateHolderArity) + { + _ = sb.Append($" var {t} = _holder[{index}].Value;\n"); + } + else + { + _ = sb.Append($" var {t} = _holder.Value{index + 1};\n"); + } } } @@ -156,7 +177,7 @@ private string GenFormatFunc(LoggerMethod lm) private static readonly global::System.Func<{typeName}, global::System.Exception?, string> _format{lm.Name} = (_holder, _) => {{ {sb} - return $""{EscapeMessageString(lm.Message)}""; + return $""{EscapeMessageString(lm.Message!)}""; }}; "; } @@ -168,7 +189,7 @@ private string GenFormatFunc(LoggerMethod lm) private string GenNameArray(LoggerMethod lm) { - if (lm.Parameters.Count is < MinStateHolderWithNameArray or > MaxStateHolderArity) + if (lm.RegularParameters.Count is < MinStateHolderWithNameArray or > MaxStateHolderArity) { return string.Empty; } @@ -178,7 +199,7 @@ private string GenNameArray(LoggerMethod lm) { _ = sb.Append("\n [global::System.Runtime.CompilerServices.CompilerGenerated]\n"); _ = sb.Append($" private static readonly string[] _names{lm.Name} = new[] {{ "); - foreach (var p in lm.Parameters) + foreach (var p in lm.RegularParameters) { _ = sb.Append($"\"{NormalizeArgumentName(p.Name)}\", "); } @@ -198,7 +219,7 @@ private string GenLogMethod(LoggerMethod lm) if (lm.Level == null) { - foreach (var p in lm.Parameters) + foreach (var p in lm.AllParameters) { if (p.IsLogLevel) { @@ -235,7 +256,7 @@ private string GenLogMethod(LoggerMethod lm) } string exceptionArg = "null"; - foreach (var p in lm.Parameters) + foreach (var p in lm.AllParameters) { if (p.IsException) { @@ -245,23 +266,33 @@ private string GenLogMethod(LoggerMethod lm) } string formatFunc; - if (lm.MessageHasTemplates) + if (lm.Templates.Count != 0) { formatFunc = $"_format{lm.Name}"; } else { - formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message)}\""; + formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message!)}\""; + } + + string loggerArg = string.Empty; + foreach (var p in lm.AllParameters) + { + if (p.IsLogger) + { + loggerArg = p.Name; + break; + } } #pragma warning disable S103 // Lines should not be too long return $@" [global::System.Runtime.CompilerServices.CompilerGenerated] - {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{lm.LoggerType} {lm.LoggerName}{(lm.Parameters.Count > 0 ? ", " : string.Empty)}{GenParameters(lm)}) + {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{GenParameters(lm)}) {{ - if ({lm.LoggerName}.IsEnabled({level})) + if ({loggerArg}.IsEnabled({level})) {{ - {lm.LoggerName}.Log( + {loggerArg}.Log( {level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), {GenHolder(lm, formatFunc)}, @@ -273,14 +304,52 @@ private string GenLogMethod(LoggerMethod lm) #pragma warning restore S103 // Lines should not be too long } + private void AutoGenerateMessage(LoggerMethod lm) + { + if (!string.IsNullOrEmpty(lm.Message)) + { + // already got a message + return; + } + + if (lm.RegularParameters.Count == 0) + { + lm.Message = "{}"; + return; + } + + var sb = GetStringBuilder(); + try + { + sb.Append("{{"); + foreach (var p in lm.RegularParameters) + { + if (p != lm.RegularParameters[0]) + { + sb.Append(','); + } + + _ = sb.Append($"\"{p.Name}\":\"{{{p.Name}}}\""); + lm.Templates.Add(p.Name); + } + + sb.Append("}}"); + lm.Message = sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + private string GenParameters(LoggerMethod lm) { var sb = GetStringBuilder(); try { - foreach (var p in lm.Parameters) + foreach (var p in lm.AllParameters) { - if (p != lm.Parameters[0]) + if (p != lm.AllParameters[0]) { _ = sb.Append(", "); } @@ -298,24 +367,24 @@ private string GenParameters(LoggerMethod lm) private string GenHolder(LoggerMethod lm, string formatFunc) { - if (lm.Parameters.Count == 0) + if (lm.RegularParameters.Count == 0) { return $"new global::{StateHolderNamespace}.LogStateHolder({formatFunc})"; } - if (lm.Parameters.Count == 1) + if (lm.RegularParameters.Count == 1) { - return $"new global::{StateHolderNamespace}.LogStateHolder<{lm.Parameters[0].Type}>" + - $"({formatFunc}, \"{NormalizeArgumentName(lm.Parameters[0].Name)}\", {lm.Parameters[0].Name})"; + return $"new global::{StateHolderNamespace}.LogStateHolder<{lm.RegularParameters[0].Type}>" + + $"({formatFunc}, \"{NormalizeArgumentName(lm.RegularParameters[0].Name)}\", {lm.RegularParameters[0].Name})"; } var sb = GetStringBuilder(); try { - if (lm.Parameters.Count > MaxStateHolderArity) + if (lm.RegularParameters.Count > MaxStateHolderArity) { _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolderN({formatFunc}, new global::System.Collections.Generic.KeyValuePair[] {{ "); - foreach (var p in lm.Parameters) + foreach (var p in lm.RegularParameters) { _ = sb.Append($"new (\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); } @@ -324,9 +393,9 @@ private string GenHolder(LoggerMethod lm, string formatFunc) } else { - foreach (var p in lm.Parameters) + foreach (var p in lm.RegularParameters) { - if (p != lm.Parameters[0]) + if (p != lm.RegularParameters[0]) { _ = sb.Append(", "); } @@ -338,9 +407,9 @@ private string GenHolder(LoggerMethod lm, string formatFunc) _ = sb.Clear(); _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolder<{tp}>({formatFunc}, _names{lm.Name}, "); - foreach (var p in lm.Parameters) + foreach (var p in lm.RegularParameters) { - if (p != lm.Parameters[0]) + if (p != lm.RegularParameters[0]) { _ = sb.Append(", "); } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 667d3ff015a1b..637f8fcd8219b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -111,12 +111,6 @@ public IReadOnlyList GetLogClasses(IEnumerable? templates = null; - if (!string.IsNullOrWhiteSpace(message)) - { - templates = ExtractTemplateArgs(message); - } - var lm = new LoggerMethod { Name = method.Identifier.ToString(), @@ -124,11 +118,12 @@ public IReadOnlyList GetLogClasses(IEnumerable 0, IsExtensionMethod = methodSymbol.IsExtensionMethod, Modifiers = method.Modifiers.ToString(), }; + ExtractTemplates(message, lm.Templates); + bool keepMethod = true; // whether or not we want to keep the method definition or if it's got errors making it so we should discard it instead if (lm.Name[0] == '_') { @@ -197,9 +192,10 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable /// Finds the template arguments contained in the message string. /// - private static List ExtractTemplateArgs(string message) + private static void ExtractTemplates(string? message, HashSet templates) { - var args = new List(); + if (string.IsNullOrEmpty(message)) + { + return; + } + var scanIndex = 0; - var endIndex = message.Length; + var endIndex = message!.Length; while (scanIndex < endIndex) { @@ -523,12 +525,10 @@ private static List ExtractTemplateArgs(string message) // Format item syntax : { index[,alignment][ :formatString] }. var formatDelimiterIndex = FindIndexOfAny(message, _formatDelimiters, openBraceIndex, closeBraceIndex); - args.Add(message.Substring(openBraceIndex + 1, formatDelimiterIndex - openBraceIndex - 1)); + _ = templates.Add(message.Substring(openBraceIndex + 1, formatDelimiterIndex - openBraceIndex - 1)); scanIndex = closeBraceIndex + 1; } } - - return args; } private static int FindBraceIndex(string message, char brace, int startIndex, int endIndex) @@ -606,17 +606,16 @@ internal class LoggerClass /// internal class LoggerMethod { - public readonly List Parameters = new (); + public readonly List AllParameters = new (); + public readonly List RegularParameters = new (); public string Name = string.Empty; - public string Message = string.Empty; + public string? Message; public int? Level; public int EventId; - public string EventName = string.Empty; - public bool MessageHasTemplates; + public string? EventName; + public readonly HashSet Templates = new (); public bool IsExtensionMethod; public string Modifiers = string.Empty; - public string LoggerType = string.Empty; - public string LoggerName = string.Empty; } /// @@ -626,8 +625,10 @@ internal class LoggerParameter { public string Name = string.Empty; public string Type = string.Empty; + public bool IsLogger; public bool IsException; public bool IsLogLevel; + public bool IsRegular => !IsLogger && !IsException && !IsLogLevel; } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs index b69ff47509908..a866a8db259f4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs @@ -99,72 +99,54 @@ internal static string DontMentionExceptionInMessageTitle { /// /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. /// - internal static string DontMentionLogLevelInMessageMessage { - get { - return ResourceManager.GetString("DontMentionLogLevelInMessageMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include log level parameters as templates in the logging message.. - /// - internal static string DontMentionLogLevelInMessageTitle { - get { - return ResourceManager.GetString("DontMentionLogLevelInMessageTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Multiple logging messages are using event id {0}. - /// - internal static string ErrorEventIdReuseMessage { + internal static string DontMentionLoggerInMessageMessage { get { - return ResourceManager.GetString("ErrorEventIdReuseMessage", resourceCulture); + return ResourceManager.GetString("DontMentionLoggerInMessageMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Multiple logging messages cannot use the same event id. + /// Looks up a localized string similar to Don't include logger parameters as templates in the logging message.. /// - internal static string ErrorEventIdReuseTitle { + internal static string DontMentionLoggerInMessageTitle { get { - return ResourceManager.GetString("ErrorEventIdReuseTitle", resourceCulture); + return ResourceManager.GetString("DontMentionLoggerInMessageTitle", resourceCulture); } } /// - /// Looks up a localized string similar to The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. + /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. /// - internal static string ErrorFirstArgMustBeILoggerMessage { + internal static string DontMentionLogLevelInMessageMessage { get { - return ResourceManager.GetString("ErrorFirstArgMustBeILoggerMessage", resourceCulture); + return ResourceManager.GetString("DontMentionLogLevelInMessageMessage", resourceCulture); } } /// - /// Looks up a localized string similar to The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. + /// Looks up a localized string similar to Don't include log level parameters as templates in the logging message.. /// - internal static string ErrorFirstArgMustBeILoggerTitle { + internal static string DontMentionLogLevelInMessageTitle { get { - return ResourceManager.GetString("ErrorFirstArgMustBeILoggerTitle", resourceCulture); + return ResourceManager.GetString("DontMentionLogLevelInMessageTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Missing message for logging method {0}. + /// Looks up a localized string similar to Multiple logging messages are using event id {0}. /// - internal static string ErrorInvalidMessageMessage { + internal static string ErrorEventIdReuseMessage { get { - return ResourceManager.GetString("ErrorInvalidMessageMessage", resourceCulture); + return ResourceManager.GetString("ErrorEventIdReuseMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Missing message for logging method. + /// Looks up a localized string similar to Multiple logging messages cannot use the same event id. /// - internal static string ErrorInvalidMessageTitle { + internal static string ErrorEventIdReuseTitle { get { - return ResourceManager.GetString("ErrorInvalidMessageTitle", resourceCulture); + return ResourceManager.GetString("ErrorEventIdReuseTitle", resourceCulture); } } @@ -258,6 +240,24 @@ internal static string ErrorMethodIsGenericTitle { } } + /// + /// Looks up a localized string similar to One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. + /// + internal static string ErrorMissingLoggerMessage { + get { + return ResourceManager.GetString("ErrorMissingLoggerMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. + /// + internal static string ErrorMissingLoggerTitle { + get { + return ResourceManager.GetString("ErrorMissingLoggerTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. /// diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index f5abfbcdb3451..df0df58f9fe71 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -123,12 +123,6 @@ Logging method names cannot start with _ - - Missing message for logging method - - - Missing message for logging method {0} - Logging method parameter names cannot start with _ @@ -159,11 +153,11 @@ Logging methods must return void - - The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface + + One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - The first argument to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface + + One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface Logging methods must be static @@ -231,4 +225,10 @@ Don't include log level parameters as templates in the logging message. + + Don't include a template for {0} in the logging message since it is implicitly taken care of + + + Don't include logger parameters as templates in the logging message. + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs deleted file mode 100644 index 2f26cb1b8c1dc..0000000000000 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Definitions.cs +++ /dev/null @@ -1,229 +0,0 @@ -// © Microsoft Corporation. All rights reserved. - -using System; -using Microsoft.Extensions.Logging; - -// NOTE: This source file serves two purposes: -// -// #1. It is used to trigger the source generator during compilation of the test suite itself. The resulting generated code -// is then tested by LoggerMessageGeneratedCodeTests.cs. This ensures the generated code works reliably. -// -// #2. It is loaded as a file from LoggerMessageGeneratorEmitterTests.cs, and then fed manually to the parser and then the generator. -// This is used strictly to calculate code coverage attained by the #1 case above. - -#pragma warning disable CA1801 // Review unused parameters -#pragma warning disable S1118 // Utility classes should not have public constructors -#pragma warning disable S3903 // Types should be defined in named namespaces -#pragma warning disable SA1202 // Elements should be ordered by access -#pragma warning disable SA1204 // Static elements should appear before instance elements -#pragma warning disable SA1207 // Protected should come before internal -#pragma warning disable SA1402 // File may only contain a single type -#pragma warning disable SA1403 // File may only contain a single namespace - -// Used to test use outside of a namespace -internal static partial class NoNamespace -{ - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - public static partial void CouldNotOpenSocket(ILogger logger, string hostName); -} - -namespace Level1 -{ - // used to test use inside a one-level namespace - internal static partial class OneLevelNamespace - { - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - public static partial void CouldNotOpenSocket(ILogger logger, string hostName); - } -} - -namespace Level1 -{ - namespace Level2 - { - // used to test use inside a two-level namespace - internal static partial class TwoLevelNamespace - { - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] - public static partial void CouldNotOpenSocket(ILogger logger, string hostName); - } - } -} - -namespace Microsoft.Extensions.Logging.Generators.Test -{ - // test particular method signature variations are generated correctly - internal partial class SignatureTests - where T : class - { - public static void Combo(ILogger logger, ILogger logger2) - { - M1(logger); - M2(logger); - M3(logger); - M4(logger2); - M5(logger, new[] { "A" }); - M6(logger); - M8(logger); - M9(logger); - M10(logger, null); - M11(logger, "A", LogLevel.Debug, "B"); - } - - // normal public method - [LoggerMessage(0, LogLevel.Critical, "Message1")] - public static partial void M1(ILogger logger); - - // internal method - [LoggerMessage(1, LogLevel.Critical, "Message2")] - internal static partial void M2(ILogger logger); - - // private method - [LoggerMessage(2, LogLevel.Critical, "Message3")] - private static partial void M3(ILogger logger); - - // generic ILogger - [LoggerMessage(3, LogLevel.Critical, "Message4")] - private static partial void M4(ILogger logger); - - // random type method parameter - [LoggerMessage(4, LogLevel.Critical, "Message5 {items}")] - private static partial void M5(ILogger logger, System.Collections.IEnumerable items); - - // line feeds and quotes in the message string - [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] - private static partial void M6(ILogger logger); - - // generic parameter - [LoggerMessage(6, LogLevel.Critical, "Message7 {p1}\n\"\r")] - private static partial void M7(ILogger logger, T p1); - - // normal public method - [LoggerMessage(7, LogLevel.Critical, "Message8")] - private protected static partial void M8(ILogger logger); - - // internal method - [LoggerMessage(8, LogLevel.Critical, "Message9")] - protected internal static partial void M9(ILogger logger); - - // nullable parameter - [LoggerMessage(9, LogLevel.Critical, "Message10 {optional}")] - internal static partial void M10(ILogger logger, string? optional); - - // dynamic log level - [LoggerMessage(10, "Message11 {p1} {p2}")] - internal static partial void M11(ILogger logger, string p1, LogLevel level, string p2); - } - - // test particular method signature variations are generated correctly - internal static partial class SignatureTests - { - // extension method - [LoggerMessage(eventId: 10, level: LogLevel.Critical, message: "Message11")] - internal static partial void M11(this ILogger logger); - - public static void Combo(ILogger logger) - { - logger.M11(); - } - } - - internal static partial class ArgTestExtensions - { - [LoggerMessage(0, LogLevel.Error, "M1")] - public static partial void Method1(ILogger logger); - - [LoggerMessage(1, LogLevel.Error, "M2 {p1}")] - public static partial void Method2(ILogger logger, string p1); - - [LoggerMessage(2, LogLevel.Error, "M3 {p1} {p2}")] - public static partial void Method3(ILogger logger, string p1, int p2); - - [LoggerMessage(3, LogLevel.Error, "M4")] - public static partial void Method4(ILogger logger, InvalidOperationException p1); - - [LoggerMessage(4, LogLevel.Error, "M5 {p2}")] - public static partial void Method5(ILogger logger, InvalidOperationException p1, InvalidOperationException p2); - - [LoggerMessage(5, LogLevel.Error, "M6 {p2}")] - public static partial void Method6(ILogger logger, InvalidOperationException p1, int p2); - - [LoggerMessage(6, LogLevel.Error, "M7 {p1}")] - public static partial void Method7(ILogger logger, int p1, InvalidOperationException p2); - - [LoggerMessage(7, LogLevel.Error, "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] -#pragma warning disable S107 // Methods should not have too many parameters - public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); -#pragma warning restore S107 // Methods should not have too many parameters - - [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] -#pragma warning disable S107 // Methods should not have too many parameters - public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); -#pragma warning restore S107 // Methods should not have too many parameters - - [LoggerMessage(9, LogLevel.Error, "M10{p1}")] - public static partial void Method10(ILogger logger, int p1); - } - - internal static partial class ReadOnlyListExtensions - { - [LoggerMessage(0, LogLevel.Error, "M0")] - public static partial void M0(ILogger logger); - - [LoggerMessage(1, LogLevel.Error, "M1{p0}")] - public static partial void M1(ILogger logger, int p0); - - [LoggerMessage(2, LogLevel.Error, "M2{p0}{p1}")] - public static partial void M2(ILogger logger, int p0, int p1); - - [LoggerMessage(3, LogLevel.Error, "M3{p0}{p1}{p2}")] - public static partial void M3(ILogger logger, int p0, int p1, int p2); - - [LoggerMessage(4, LogLevel.Error, "M4{p0}{p1}{p2}{p3}")] - public static partial void M4(ILogger logger, int p0, int p1, int p2, int p3); - - [LoggerMessage(5, LogLevel.Error, "M5{p0}{p1}{p2}{p3}{p4}")] - public static partial void M5(ILogger logger, int p0, int p1, int p2, int p3, int p4); - - [LoggerMessage(6, LogLevel.Error, "M6{p0}{p1}{p2}{p3}{p4}{p5}")] - public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); - - [LoggerMessage(7, LogLevel.Error, "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] -#pragma warning disable S107 // Methods should not have too many parameters - public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); -#pragma warning restore S107 // Methods should not have too many parameters - } - - internal static partial class LevelTestExtensions - { - [LoggerMessage(0, LogLevel.Trace, "M0")] - public static partial void M0(ILogger logger); - - [LoggerMessage(1, LogLevel.Debug, "M1")] - public static partial void M1(ILogger logger); - - [LoggerMessage(2, LogLevel.Information, "M2")] - public static partial void M2(ILogger logger); - - [LoggerMessage(3, LogLevel.Warning, "M3")] - public static partial void M3(ILogger logger); - - [LoggerMessage(4, LogLevel.Error, "M4")] - public static partial void M4(ILogger logger); - - [LoggerMessage(5, LogLevel.Critical, "M5")] - public static partial void M5(ILogger logger); - - [LoggerMessage(6, LogLevel.None, "M6")] - public static partial void M6(ILogger logger); - - [LoggerMessage(7, (LogLevel)42, "M7")] - public static partial void M7(ILogger logger); - } - - internal static partial class EventNameTestExtensions - { - [LoggerMessage(0, LogLevel.Trace, "M0", EventName = "CustomEventName")] - public static partial void M0(ILogger logger); - } -} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 2caa82c3efb40..9933da3b74628 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Generators.Test.TestClasses; using Xunit; namespace Microsoft.Extensions.Logging.Generators.Test @@ -111,44 +112,79 @@ public void ArgTest() } [Fact] - public void ReadOnlyListTest() + public void CollectionTest() { var logger = new MockLogger(); logger.Reset(); - ReadOnlyListExtensions.M0(logger); + CollectionTestExtensions.M0(logger); TestCollection(0, logger); logger.Reset(); - ReadOnlyListExtensions.M1(logger, 0); + CollectionTestExtensions.M1(logger, 0); TestCollection(1, logger); logger.Reset(); - ReadOnlyListExtensions.M2(logger, 0, 1); + CollectionTestExtensions.M2(logger, 0, 1); TestCollection(2, logger); logger.Reset(); - ReadOnlyListExtensions.M3(logger, 0, 1, 2); + CollectionTestExtensions.M3(logger, 0, 1, 2); TestCollection(3, logger); logger.Reset(); - ReadOnlyListExtensions.M4(logger, 0, 1, 2, 3); + CollectionTestExtensions.M4(logger, 0, 1, 2, 3); TestCollection(4, logger); logger.Reset(); - ReadOnlyListExtensions.M5(logger, 0, 1, 2, 3, 4); + CollectionTestExtensions.M5(logger, 0, 1, 2, 3, 4); TestCollection(5, logger); logger.Reset(); - ReadOnlyListExtensions.M6(logger, 0, 1, 2, 3, 4, 5); + CollectionTestExtensions.M6(logger, 0, 1, 2, 3, 4, 5); TestCollection(6, logger); logger.Reset(); - ReadOnlyListExtensions.M7(logger, 0, 1, 2, 3, 4, 5, 6); + CollectionTestExtensions.M7(logger, 0, 1, 2, 3, 4, 5, 6); TestCollection(7, logger); - // TestCollection is doing all the testing for us - Assert.True(true); + logger.Reset(); + CollectionTestExtensions.M8(logger, LogLevel.Critical, 0, new ArgumentException("Foo"), 1); + TestCollection(2, logger); + } + + [Fact] + public void MessageTests() + { + var logger = new MockLogger(); + + logger.Reset(); + MessageTestExtensions.M0(logger); + Assert.Null(logger.LastException); + Assert.Equal("{}", logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + MessageTestExtensions.M1(logger); + Assert.Null(logger.LastException); + Assert.Equal("{}", logger.LastFormattedString); + Assert.Equal(LogLevel.Debug, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + MessageTestExtensions.M2(logger, "Foo", "Bar"); + Assert.Null(logger.LastException); + Assert.Equal("{\"p1\":\"Foo\",\"p2\":\"Bar\"}", logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + MessageTestExtensions.M3(logger, "Foo", 42); + Assert.Null(logger.LastException); + Assert.Equal("{\"p1\":\"Foo\",\"p2\":\"42\"}", logger.LastFormattedString); + Assert.Equal(LogLevel.Debug, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); } [Fact] @@ -211,6 +247,40 @@ public void LevelTests() Assert.Equal("M7", logger.LastFormattedString); Assert.Equal((LogLevel)42, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M8(logger, LogLevel.Critical); + Assert.Null(logger.LastException); + Assert.Equal("M8", logger.LastFormattedString); + Assert.Equal(LogLevel.Critical, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + LevelTestExtensions.M9(LogLevel.Trace, logger); + Assert.Null(logger.LastException); + Assert.Equal("M9", logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + } + + [Fact] + public void ExceptionTests() + { + var logger = new MockLogger(); + + logger.Reset(); + ExceptionTestExtensions.M0(logger, new ArgumentException("Foo"), new ArgumentException("Bar")); + Assert.Equal("Foo", logger.LastException!.Message); + Assert.Equal("M0 System.ArgumentException: Bar", logger.LastFormattedString); + Assert.Equal(LogLevel.Trace, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); + + logger.Reset(); + ExceptionTestExtensions.M1(new ArgumentException("Foo"), logger, new ArgumentException("Bar")); + Assert.Equal("Foo", logger.LastException!.Message); + Assert.Equal("M1 System.ArgumentException: Bar", logger.LastFormattedString); + Assert.Equal(LogLevel.Debug, logger.LastLogLevel); + Assert.Equal(1, logger.CallCount); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 154ceb4a0776d..da4f8456ff389 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -54,7 +54,13 @@ public async Task TestEmitter(string response) // attained by processing Definitions.cs. The functionality of the // resulting code is tested via LoggerMessageGeneratedCodeTests.cs - var testSourceCode = await File.ReadAllTextAsync(@"..\..\..\Definitions.cs"); + var testSourceCode = await File.ReadAllTextAsync(@"..\..\..\TestClasses\MiscTestExtensions.cs") + + await File.ReadAllTextAsync(@"..\..\..\TestClasses\LevelTestExtensions.cs") + + await File.ReadAllTextAsync(@"..\..\..\TestClasses\ArgTestExtensions.cs") + + await File.ReadAllTextAsync(@"..\..\..\TestClasses\EventNameTestExtensions.cs") + + await File.ReadAllTextAsync(@"..\..\..\TestClasses\SignatureTestExtensions.cs") + + await File.ReadAllTextAsync(@"..\..\..\TestClasses\MessageTestExtensions.cs") + + await File.ReadAllTextAsync(@"..\..\..\TestClasses\CollectionTestExtensions.cs"); var (d, r) = await RoslynTestUtils.RunGenerator( new LoggerMessageGenerator(), diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 8dd0f9ef7aeef..7cc364042118e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -62,21 +62,6 @@ static partial void M1(ILogger logger) Assert.Equal("LG0016", d[0].Id); } - [Fact] - public async Task InvalidMessage() - { - var d = await RunGenerator(@" - partial class C - { - [LoggerMessage(0, LogLevel.Debug, """")] - static partial void M1(ILogger logger); - } - "); - - Assert.Single(d); - Assert.Equal("LG0001", d[0].Id); - } - [Fact] public async Task MissingTemplate() { @@ -134,7 +119,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0013", d[0].Id); + Assert.Equal(DiagDescriptors.DontMentionExceptionInMessage.Id, d[0].Id); } [Fact] @@ -149,7 +134,22 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0018", d[0].Id); + Assert.Equal(DiagDescriptors.DontMentionLogLevelInMessage.Id, d[0].Id); + } + + [Fact] + public async Task NeedlessLoggerInMessage() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1 {logger}"")] + static partial void M1(ILogger logger); + } + "); + + Assert.Single(d); + Assert.Equal(DiagDescriptors.DontMentionLoggerInMessage.Id, d[0].Id); } [Fact] @@ -272,7 +272,7 @@ partial class C public async Task MissingILoggerType() { var d = await RunGenerator(@" - namespace Microsoft.R9.Extensions.Logging + namespace Microsoft.Extensions.Logging { public sealed class LoggerMessageAttribute : System.Attribute {} } @@ -288,7 +288,7 @@ partial class C public async Task MissingLogLevelType() { var d = await RunGenerator(@" - namespace Microsoft.R9.Extensions.Logging + namespace Microsoft.Extensions.Logging { public sealed class LoggerMessageAttribute : System.Attribute {} } @@ -340,13 +340,13 @@ partial class C } [Fact] - public async Task FirstArgILogger() + public async Task MissingILogger() { var d = await RunGenerator(@" partial class C { - [LoggerMessage(0, LogLevel.Debug, ""M1 {p1} {logger}"")] - static partial void M1(int p1, ILogger logger); + [LoggerMessage(0, LogLevel.Debug, ""M1 {p1}"")] + static partial void M1(int p1); } "); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs new file mode 100644 index 0000000000000..0f04d525de039 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs @@ -0,0 +1,45 @@ +// © Microsoft Corporation. All rights reserved. + +using System; + +#pragma warning disable CA1801 // Review unused parameters + +namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +{ + internal static partial class ArgTestExtensions + { + [LoggerMessage(0, LogLevel.Error, "M1")] + public static partial void Method1(ILogger logger); + + [LoggerMessage(1, LogLevel.Error, "M2 {p1}")] + public static partial void Method2(ILogger logger, string p1); + + [LoggerMessage(2, LogLevel.Error, "M3 {p1} {p2}")] + public static partial void Method3(ILogger logger, string p1, int p2); + + [LoggerMessage(3, LogLevel.Error, "M4")] + public static partial void Method4(ILogger logger, InvalidOperationException p1); + + [LoggerMessage(4, LogLevel.Error, "M5 {p2}")] + public static partial void Method5(ILogger logger, System.InvalidOperationException p1, System.InvalidOperationException p2); + + [LoggerMessage(5, LogLevel.Error, "M6 {p2}")] + public static partial void Method6(ILogger logger, System.InvalidOperationException p1, int p2); + + [LoggerMessage(6, LogLevel.Error, "M7 {p1}")] + public static partial void Method7(ILogger logger, int p1, System.InvalidOperationException p2); + + [LoggerMessage(7, LogLevel.Error, "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] +#pragma warning disable S107 // Methods should not have too many parameters + public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); +#pragma warning restore S107 // Methods should not have too many parameters + + [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] +#pragma warning disable S107 // Methods should not have too many parameters + public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); +#pragma warning restore S107 // Methods should not have too many parameters + + [LoggerMessage(9, LogLevel.Error, "M10{p1}")] + public static partial void Method10(ILogger logger, int p1); + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs new file mode 100644 index 0000000000000..a8b750ac66d4d --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs @@ -0,0 +1,40 @@ +// © Microsoft Corporation. All rights reserved. + +using System; + +#pragma warning disable CA1801 // Review unused parameters + +namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +{ + internal static partial class CollectionTestExtensions + { + [LoggerMessage(0, LogLevel.Error, "M0")] + public static partial void M0(ILogger logger); + + [LoggerMessage(1, LogLevel.Error, "M1{p0}")] + public static partial void M1(ILogger logger, int p0); + + [LoggerMessage(2, LogLevel.Error, "M2{p0}{p1}")] + public static partial void M2(ILogger logger, int p0, int p1); + + [LoggerMessage(3, LogLevel.Error, "M3{p0}{p1}{p2}")] + public static partial void M3(ILogger logger, int p0, int p1, int p2); + + [LoggerMessage(4, LogLevel.Error, "M4{p0}{p1}{p2}{p3}")] + public static partial void M4(ILogger logger, int p0, int p1, int p2, int p3); + + [LoggerMessage(5, LogLevel.Error, "M5{p0}{p1}{p2}{p3}{p4}")] + public static partial void M5(ILogger logger, int p0, int p1, int p2, int p3, int p4); + + [LoggerMessage(6, LogLevel.Error, "M6{p0}{p1}{p2}{p3}{p4}{p5}")] + public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); + + [LoggerMessage(7, LogLevel.Error, "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] +#pragma warning disable S107 // Methods should not have too many parameters + public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); +#pragma warning restore S107 // Methods should not have too many parameters + + [LoggerMessage(8, "M8{p0}{p1}")] + public static partial void M8(ILogger logger, LogLevel level, int p0, System.Exception ex, int p1); + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs new file mode 100644 index 0000000000000..d3abb21a6892f --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs @@ -0,0 +1,12 @@ +// © Microsoft Corporation. All rights reserved. + +#pragma warning disable CA1801 // Review unused parameters + +namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +{ + internal static partial class EventNameTestExtensions + { + [LoggerMessage(0, LogLevel.Trace, "M0", EventName = "CustomEventName")] + public static partial void M0(ILogger logger); + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs new file mode 100644 index 0000000000000..5313c2cf7289d --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs @@ -0,0 +1,17 @@ +// © Microsoft Corporation. All rights reserved. + +using System; + +#pragma warning disable CA1801 // Review unused parameters + +namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +{ + internal static partial class ExceptionTestExtensions + { + [LoggerMessage(0, LogLevel.Trace, "M0 {ex2}")] + public static partial void M0(ILogger logger, Exception ex1, Exception ex2); + + [LoggerMessage(1, LogLevel.Debug, "M1 {ex2}")] + public static partial void M1(Exception ex1, ILogger logger, Exception ex2); + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs new file mode 100644 index 0000000000000..6a339e1f82f89 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs @@ -0,0 +1,39 @@ +// © Microsoft Corporation. All rights reserved. + +#pragma warning disable CA1801 // Review unused parameters + +namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +{ + internal static partial class LevelTestExtensions + { + [LoggerMessage(0, LogLevel.Trace, "M0")] + public static partial void M0(ILogger logger); + + [LoggerMessage(1, LogLevel.Debug, "M1")] + public static partial void M1(ILogger logger); + + [LoggerMessage(2, LogLevel.Information, "M2")] + public static partial void M2(ILogger logger); + + [LoggerMessage(3, LogLevel.Warning, "M3")] + public static partial void M3(ILogger logger); + + [LoggerMessage(4, LogLevel.Error, "M4")] + public static partial void M4(ILogger logger); + + [LoggerMessage(5, LogLevel.Critical, "M5")] + public static partial void M5(ILogger logger); + + [LoggerMessage(6, LogLevel.None, "M6")] + public static partial void M6(ILogger logger); + + [LoggerMessage(7, (LogLevel)42, "M7")] + public static partial void M7(ILogger logger); + + [LoggerMessage(8, "M8")] + public static partial void M8(ILogger logger, LogLevel level); + + [LoggerMessage(9, "M9")] + public static partial void M9(LogLevel level, ILogger logger); + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs new file mode 100644 index 0000000000000..8c2bfc42c6bed --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -0,0 +1,21 @@ +// © Microsoft Corporation. All rights reserved. + +#pragma warning disable CA1801 // Review unused parameters + +namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +{ + internal static partial class MessageTestExtensions + { + [LoggerMessage(0, LogLevel.Trace)] + public static partial void M0(ILogger logger); + + [LoggerMessage(1, LogLevel.Debug, "")] + public static partial void M1(ILogger logger); + + [LoggerMessage(2, LogLevel.Trace)] + public static partial void M2(ILogger logger, string p1, string p2); + + [LoggerMessage(3, LogLevel.Debug, "")] + public static partial void M3(ILogger logger, string p1, int p2); + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs new file mode 100644 index 0000000000000..9228199c725a9 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs @@ -0,0 +1,42 @@ +// © Microsoft Corporation. All rights reserved. + +using Microsoft.Extensions.Logging; + +#pragma warning disable CA1801 // Review unused parameters +#pragma warning disable S1118 // Utility classes should not have public constructors +#pragma warning disable S3903 // Types should be defined in named namespaces +#pragma warning disable SA1202 // Elements should be ordered by access +#pragma warning disable SA1204 // Static elements should appear before instance elements +#pragma warning disable SA1207 // Protected should come before internal +#pragma warning disable SA1402 // File may only contain a single type +#pragma warning disable SA1403 // File may only contain a single namespace + +// Used to test use outside of a namespace +internal static partial class NoNamespace +{ + [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + public static partial void CouldNotOpenSocket(ILogger logger, string hostName); +} + +namespace Level1 +{ + // used to test use inside a one-level namespace + internal static partial class OneLevelNamespace + { + [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + public static partial void CouldNotOpenSocket(ILogger logger, string hostName); + } +} + +namespace Level1 +{ + namespace Level2 + { + // used to test use inside a two-level namespace + internal static partial class TwoLevelNamespace + { + [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + public static partial void CouldNotOpenSocket(ILogger logger, string hostName); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/README.md b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/README.md new file mode 100644 index 0000000000000..f6cf4fd21bc25 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/README.md @@ -0,0 +1,7 @@ +The source files in this directory serve two purposes: + +1. They are used to trigger the source generator during compilation of the test suite itself. The resulting generated code +is then tested by LoggerMessageGeneratedCodeTests.cs. This ensures the generated code works reliably. + +2.They are loaded as a file from `LoggerMessageGeneratorEmitterTests.cs`, and then fed manually to the parser and then the generator +This is used strictly to calculate code coverage attained by the first case above. diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs new file mode 100644 index 0000000000000..700515d3660ca --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs @@ -0,0 +1,82 @@ +// © Microsoft Corporation. All rights reserved. + +#pragma warning disable CA1801 // Review unused parameters + +namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +{ + // test particular method signature variations are generated correctly + internal partial class SignatureTestExtensions + where T : class + { + public static void Combo(ILogger logger, ILogger logger2) + { + M1(logger); + M2(logger); + M3(logger); + M4(logger2); + M5(logger, new[] { "A" }); + M6(logger); + M8(logger); + M9(logger); + M10(logger, null); + M11(logger, "A", LogLevel.Debug, "B"); + } + + // normal public method + [LoggerMessage(0, LogLevel.Critical, "Message1")] + public static partial void M1(ILogger logger); + + // internal method + [LoggerMessage(1, LogLevel.Critical, "Message2")] + internal static partial void M2(ILogger logger); + + // private method + [LoggerMessage(2, LogLevel.Critical, "Message3")] + private static partial void M3(ILogger logger); + + // generic ILogger + [LoggerMessage(3, LogLevel.Critical, "Message4")] + private static partial void M4(ILogger logger); + + // random type method parameter + [LoggerMessage(4, LogLevel.Critical, "Message5 {items}")] + private static partial void M5(ILogger logger, System.Collections.IEnumerable items); + + // line feeds and quotes in the message string + [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] + private static partial void M6(ILogger logger); + + // generic parameter + [LoggerMessage(6, LogLevel.Critical, "Message7 {p1}\n\"\r")] + private static partial void M7(ILogger logger, T p1); + + // normal public method + [LoggerMessage(7, LogLevel.Critical, "Message8")] + private protected static partial void M8(ILogger logger); + + // internal method + [LoggerMessage(8, LogLevel.Critical, "Message9")] + protected internal static partial void M9(ILogger logger); + + // nullable parameter + [LoggerMessage(9, LogLevel.Critical, "Message10 {optional}")] + internal static partial void M10(ILogger logger, string? optional); + + // dynamic log level + [LoggerMessage(10, "Message11 {p1} {p2}")] + internal static partial void M11(ILogger logger, string p1, LogLevel level, string p2); + } + + // test particular method signature variations are generated correctly + internal static partial class SignatureTestExtensions + { + // extension method + [LoggerMessage(eventId: 10, level: LogLevel.Critical, message: "Message11")] + internal static partial void M11(this ILogger logger); + + public static void Combo(ILogger logger) + { + logger.M11(); + } + } +} From 9e150d9dc3cb2c69822d84378c213a241f07e3cd Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sun, 14 Mar 2021 21:25:39 -0700 Subject: [PATCH 065/121] Add support for logging methods as instance methods --- .../gen/LoggerMessageGenerator.Emitter.cs | 2 +- .../gen/LoggerMessageGenerator.Parser.cs | 12 ++++------ .../LoggerMessageGeneratedCodeTests.cs | 21 +++++++++++++++++ .../LoggerMessageGeneratorEmitterTests.cs | 1 + .../TestClasses/TestInstances.cs | 23 +++++++++++++++++++ 5 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index f8430cbc97aa5..d1c65586b81e4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -275,7 +275,7 @@ private string GenLogMethod(LoggerMethod lm) formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message!)}\""; } - string loggerArg = string.Empty; + string loggerArg = "_logger"; foreach (var p in lm.AllParameters) { if (p.IsLogger) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 637f8fcd8219b..1278190b731b0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -162,12 +162,6 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable Date: Mon, 15 Mar 2021 06:26:56 -0700 Subject: [PATCH 066/121] Minor cleanup --- .../gen/LoggerMessageGenerator.Emitter.cs | 9 ++++--- .../gen/LoggerMessageGenerator.Parser.cs | 4 +-- .../gen/Resources.Designer.cs | 4 +-- .../gen/Resources.resx | 4 +-- .../LoggerMessageGeneratedCodeTests.cs | 4 ++- .../LoggerMessageGeneratorEmitterTests.cs | 2 +- .../LoggerMessageGeneratorParserTests.cs | 2 +- .../TestClasses/MessageTestExtensions.cs | 2 +- .../TestClasses/SignatureTestExtensions.cs | 27 ++++++++++--------- 9 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index d1c65586b81e4..8488f130d7348 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -158,6 +158,7 @@ private string GenFormatFunc(LoggerMethod lm) { break; } + index++; } @@ -321,19 +322,19 @@ private void AutoGenerateMessage(LoggerMethod lm) var sb = GetStringBuilder(); try { - sb.Append("{{"); + _ = sb.Append("{{"); foreach (var p in lm.RegularParameters) { if (p != lm.RegularParameters[0]) { - sb.Append(','); + _ = sb.Append(','); } _ = sb.Append($"\"{p.Name}\":\"{{{p.Name}}}\""); - lm.Templates.Add(p.Name); + _ = lm.Templates.Add(p.Name); } - sb.Append("}}"); + _ = sb.Append("}}"); lm.Message = sb.ToString(); } finally diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 1278190b731b0..69ff4a58f1cb5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -499,7 +499,7 @@ private bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest) /// /// Finds the template arguments contained in the message string. /// - private static void ExtractTemplates(string? message, HashSet templates) + private static void ExtractTemplates(string? message, ISet templates) { if (string.IsNullOrEmpty(message)) { @@ -606,12 +606,12 @@ internal class LoggerMethod { public readonly List AllParameters = new (); public readonly List RegularParameters = new (); + public readonly HashSet Templates = new (); public string Name = string.Empty; public string? Message; public int? Level; public int EventId; public string? EventName; - public readonly HashSet Templates = new (); public bool IsExtensionMethod; public string Modifiers = string.Empty; } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs index a866a8db259f4..1b13b667b12a2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs @@ -106,7 +106,7 @@ internal static string DontMentionLoggerInMessageMessage { } /// - /// Looks up a localized string similar to Don't include logger parameters as templates in the logging message.. + /// Looks up a localized string similar to Don't include logger parameters as templates in the logging message. /// internal static string DontMentionLoggerInMessageTitle { get { @@ -124,7 +124,7 @@ internal static string DontMentionLogLevelInMessageMessage { } /// - /// Looks up a localized string similar to Don't include log level parameters as templates in the logging message.. + /// Looks up a localized string similar to Don't include log level parameters as templates in the logging message. /// internal static string DontMentionLogLevelInMessageTitle { get { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index df0df58f9fe71..b171486f93cb4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -223,12 +223,12 @@ Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include log level parameters as templates in the logging message. + Don't include log level parameters as templates in the logging message Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include logger parameters as templates in the logging message. + Don't include logger parameters as templates in the logging message \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index e038c578f9b20..98ef3300b19a2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -1,4 +1,4 @@ -// � Microsoft Corporation. All rights reserved. +// © Microsoft Corporation. All rights reserved. using System; using System.Collections.Generic; @@ -151,6 +151,8 @@ public void CollectionTest() logger.Reset(); CollectionTestExtensions.M8(logger, LogLevel.Critical, 0, new ArgumentException("Foo"), 1); TestCollection(2, logger); + + Assert.True(true); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 841ec30a24bb4..708cebcec641c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Logging.Generators.Test { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Test")] public class LoggerMessageGeneratorEmitterTests { private class Options : AnalyzerConfigOptions diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 7cc364042118e..266bca48feb33 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.Extensions.Logging.Generators.Test { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Test")] public class LoggerMessageGeneratorParserTests { [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index 8c2bfc42c6bed..6395f7e8cc809 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -11,7 +11,7 @@ internal static partial class MessageTestExtensions [LoggerMessage(1, LogLevel.Debug, "")] public static partial void M1(ILogger logger); - + [LoggerMessage(2, LogLevel.Trace)] public static partial void M2(ILogger logger, string p1, string p2); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs index 700515d3660ca..f161f2fd962cf 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs @@ -1,9 +1,23 @@ // © Microsoft Corporation. All rights reserved. #pragma warning disable CA1801 // Review unused parameters +#pragma warning disable S1118 namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { + // test particular method signature variations are generated correctly + internal static partial class SignatureTestExtensions + { + // extension method + [LoggerMessage(eventId: 10, level: LogLevel.Critical, message: "Message11")] + internal static partial void M11(this ILogger logger); + + public static void Combo(ILogger logger) + { + logger.M11(); + } + } + // test particular method signature variations are generated correctly internal partial class SignatureTestExtensions where T : class @@ -66,17 +80,4 @@ public static void Combo(ILogger logger, ILogger logger2) [LoggerMessage(10, "Message11 {p1} {p2}")] internal static partial void M11(ILogger logger, string p1, LogLevel level, string p2); } - - // test particular method signature variations are generated correctly - internal static partial class SignatureTestExtensions - { - // extension method - [LoggerMessage(eventId: 10, level: LogLevel.Critical, message: "Message11")] - internal static partial void M11(this ILogger logger); - - public static void Combo(ILogger logger) - { - logger.M11(); - } - } } From cc441922b983fabdc412861484b15f914e046439 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 15 Mar 2021 20:33:18 -0700 Subject: [PATCH 067/121] Various improvements - Add support for the EmitDefaultMessages generator option which controls whether default messages are produced if the user doesn't supply one. - Add support for the FieldName generator option which determines the name of the field used to retrieve the logger instance when using instance mode logging methods. - Add support for the {OriginalFormat} element when enumerating the TState. This is for compatibility with the existing LoggerMessage.Define method. - Add support for automatically expanding IEnumerable logging method parameters to comma-separated strings. This is for compatibility with the LoggerMessage.Define method. --- .../gen/LoggerMessageGenerator.Emitter.cs | 65 +++++++++++++++---- .../gen/LoggerMessageGenerator.Parser.cs | 16 +++++ .../gen/LoggerMessageGenerator.cs | 19 +++++- .../LoggerMessageGeneratedCodeTests.cs | 34 ++++++---- .../LoggerMessageGeneratorParserTests.cs | 2 +- 5 files changed, 104 insertions(+), 32 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 8488f130d7348..8e9a986b61168 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Text; using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.Extensions.Logging.Generators { @@ -18,10 +19,14 @@ internal class Emitter private readonly Stack _builders = new (); private readonly bool _pascalCaseArguments; + private readonly string _fieldName; + private readonly bool _emitDefaultMessage; - public Emitter(bool pascalCaseArguments) + public Emitter(bool pascalCaseArguments, string fieldName = "_logger", bool emitDefaultMessage = true) { _pascalCaseArguments = pascalCaseArguments; + _fieldName = fieldName; + _emitDefaultMessage = emitDefaultMessage; } public string Emit(IReadOnlyList logClasses, CancellationToken cancellationToken) @@ -111,7 +116,7 @@ private string GenFormatFunc(LoggerMethod lm) { return string.Empty; } - + var sb = GetStringBuilder(); try { @@ -147,7 +152,14 @@ private string GenFormatFunc(LoggerMethod lm) { if (lm.RegularParameters.Count == 1) { - _ = sb.Append($" var {t} = _holder.Value;\n"); + if (lm.RegularParameters[0].IsEnumerable) + { + _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate(_holder.Value);\n"); + } + else + { + _ = sb.Append($" var {t} = _holder.Value;\n"); + } } else { @@ -162,13 +174,30 @@ private string GenFormatFunc(LoggerMethod lm) index++; } - if (lm.RegularParameters.Count > MaxStateHolderArity) - { - _ = sb.Append($" var {t} = _holder[{index}].Value;\n"); - } - else + if (index < lm.RegularParameters.Count) // can happen in some cases of malformed input { - _ = sb.Append($" var {t} = _holder.Value{index + 1};\n"); + if (lm.RegularParameters.Count > MaxStateHolderArity) + { + if (lm.RegularParameters[index].IsEnumerable) + { + _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate(_holder[{index}].Value);\n"); + } + else + { + _ = sb.Append($" var {t} = _holder[{index}].Value;\n"); + } + } + else + { + if (lm.RegularParameters[index].IsEnumerable) + { + _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate(_holder.Value{index + 1});\n"); + } + else + { + _ = sb.Append($" var {t} = _holder.Value{index + 1};\n"); + } + } } } } @@ -276,7 +305,7 @@ private string GenLogMethod(LoggerMethod lm) formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message!)}\""; } - string loggerArg = "_logger"; + string loggerArg = _fieldName; foreach (var p in lm.AllParameters) { if (p.IsLogger) @@ -313,6 +342,12 @@ private void AutoGenerateMessage(LoggerMethod lm) return; } + if (!_emitDefaultMessage) + { + lm.Message = string.Empty; + return; + } + if (lm.RegularParameters.Count == 0) { lm.Message = "{}"; @@ -368,15 +403,17 @@ private string GenParameters(LoggerMethod lm) private string GenHolder(LoggerMethod lm, string formatFunc) { + var originalFormat = EscapeMessageString(lm.Message!); + if (lm.RegularParameters.Count == 0) { - return $"new global::{StateHolderNamespace}.LogStateHolder({formatFunc})"; + return $"new global::{StateHolderNamespace}.LogStateHolder({formatFunc}, \"{originalFormat}\")"; } if (lm.RegularParameters.Count == 1) { return $"new global::{StateHolderNamespace}.LogStateHolder<{lm.RegularParameters[0].Type}>" + - $"({formatFunc}, \"{NormalizeArgumentName(lm.RegularParameters[0].Name)}\", {lm.RegularParameters[0].Name})"; + $"({formatFunc}, \"{originalFormat}\", \"{NormalizeArgumentName(lm.RegularParameters[0].Name)}\", {lm.RegularParameters[0].Name})"; } var sb = GetStringBuilder(); @@ -384,7 +421,7 @@ private string GenHolder(LoggerMethod lm, string formatFunc) { if (lm.RegularParameters.Count > MaxStateHolderArity) { - _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolderN({formatFunc}, new global::System.Collections.Generic.KeyValuePair[] {{ "); + _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolderN({formatFunc}, \"{originalFormat}\", new global::System.Collections.Generic.KeyValuePair[] {{ "); foreach (var p in lm.RegularParameters) { _ = sb.Append($"new (\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); @@ -407,7 +444,7 @@ private string GenHolder(LoggerMethod lm, string formatFunc) var tp = sb.ToString(); _ = sb.Clear(); - _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolder<{tp}>({formatFunc}, _names{lm.Name}, "); + _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolder<{tp}>({formatFunc}, \"{originalFormat}\", _names{lm.Name}, "); foreach (var p in lm.RegularParameters) { if (p != lm.RegularParameters[0]) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 69ff4a58f1cb5..510322ab71f3e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -41,6 +41,20 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable !IsLogger && !IsException && !IsLogLevel; } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs index 920769473a627..905163de592b9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs @@ -33,13 +33,26 @@ public void Execute(GeneratorExecutionContext context) } var pascalCaseArguments = false; - if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("PascalCaseArguments", out var value)) + var fieldName = "_logger"; + var emitDefaultMessage = true; + + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("PascalCaseArguments", out var pca)) { - pascalCaseArguments = (value.ToUpperInvariant() == "TRUE") || (value.ToUpperInvariant() == "YES"); + pascalCaseArguments = (pca.ToUpperInvariant() == "TRUE") || (pca.ToUpperInvariant() == "YES"); + } + + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("FieldName", out var fn)) + { + fieldName = fn; + } + + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("EmitDefaultMessage", out var edm)) + { + emitDefaultMessage = (edm.ToUpperInvariant() == "TRUE") || (edm.ToUpperInvariant() == "YES"); } var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); - var e = new Emitter(pascalCaseArguments); + var e = new Emitter(pascalCaseArguments, fieldName, emitDefaultMessage); var logClasses = p.GetLogClasses(receiver.ClassDeclarations); var result = e.Emit(logClasses, context.CancellationToken); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 98ef3300b19a2..75a1c2cf18e20 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -118,39 +118,39 @@ public void CollectionTest() logger.Reset(); CollectionTestExtensions.M0(logger); - TestCollection(0, logger); + TestCollection(1, logger); logger.Reset(); CollectionTestExtensions.M1(logger, 0); - TestCollection(1, logger); + TestCollection(2, logger); logger.Reset(); CollectionTestExtensions.M2(logger, 0, 1); - TestCollection(2, logger); + TestCollection(3, logger); logger.Reset(); CollectionTestExtensions.M3(logger, 0, 1, 2); - TestCollection(3, logger); + TestCollection(4, logger); logger.Reset(); CollectionTestExtensions.M4(logger, 0, 1, 2, 3); - TestCollection(4, logger); + TestCollection(5, logger); logger.Reset(); CollectionTestExtensions.M5(logger, 0, 1, 2, 3, 4); - TestCollection(5, logger); + TestCollection(6, logger); logger.Reset(); CollectionTestExtensions.M6(logger, 0, 1, 2, 3, 4, 5); - TestCollection(6, logger); + TestCollection(7, logger); logger.Reset(); CollectionTestExtensions.M7(logger, 0, 1, 2, 3, 4, 5, 6); - TestCollection(7, logger); + TestCollection(8, logger); logger.Reset(); CollectionTestExtensions.M8(logger, LogLevel.Critical, 0, new ArgumentException("Foo"), 1); - TestCollection(2, logger); + TestCollection(3, logger); Assert.True(true); } @@ -330,20 +330,26 @@ private static void TestCollection(int expected, MockLogger logger) Assert.Equal(expected, rol.Count); for (int i = 0; i < expected; i++) { - var kvp = new KeyValuePair($"p{i}", i); - Assert.Equal(kvp, rol[i]); + if (i != expected - 1) + { + var kvp = new KeyValuePair($"p{i}", i); + Assert.Equal(kvp, rol[i]); + } } int count = 0; foreach (var actual in rol) { - var kvp = new KeyValuePair($"p{count}", count); - Assert.Equal(kvp, actual); + if (count != expected - 1) + { + var kvp = new KeyValuePair($"p{count}", count); + Assert.Equal(kvp, actual); + } + count++; } Assert.Equal(expected, count); - Assert.Throws(() => _ = rol[expected]); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 266bca48feb33..de0696a5de2c5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -89,7 +89,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0014", d[0].Id); + Assert.Equal(DiagDescriptors.TemplateHasNoCorrespondingArgument.Id, d[0].Id); } [Fact] From b611bbd7dcb870184c436a0b256ff6e62bea4782 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 16 Mar 2021 09:29:04 -0700 Subject: [PATCH 068/121] Rename LogStateHolder to LogValues --- .../gen/LoggerMessageGenerator.Emitter.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 8e9a986b61168..cf9ac02154b2d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -11,7 +11,7 @@ public partial class LoggerMessageGenerator { internal class Emitter { - // The maximum arity of the LogStateHolder-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation + // The maximum arity of the LogValues-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation // for the array and boxing of all logging method arguments. private const int MaxStateHolderArity = 6; private const int MinStateHolderWithNameArray = 2; @@ -123,15 +123,15 @@ private string GenFormatFunc(LoggerMethod lm) string typeName; if (lm.RegularParameters.Count == 1) { - typeName = $"global::{StateHolderNamespace}.LogStateHolder<{lm.RegularParameters[0].Type}>"; + typeName = $"global::{StateHolderNamespace}.LogValues<{lm.RegularParameters[0].Type}>"; } else if (lm.RegularParameters.Count > MaxStateHolderArity) { - typeName = $"global::{StateHolderNamespace}.LogStateHolderN"; + typeName = $"global::{StateHolderNamespace}.LogValuesN"; } else { - _ = sb.Append($"global::{StateHolderNamespace}.LogStateHolder<"); + _ = sb.Append($"global::{StateHolderNamespace}.LogValues<"); foreach (var p in lm.RegularParameters) { @@ -407,12 +407,12 @@ private string GenHolder(LoggerMethod lm, string formatFunc) if (lm.RegularParameters.Count == 0) { - return $"new global::{StateHolderNamespace}.LogStateHolder({formatFunc}, \"{originalFormat}\")"; + return $"new global::{StateHolderNamespace}.LogValues({formatFunc}, \"{originalFormat}\")"; } if (lm.RegularParameters.Count == 1) { - return $"new global::{StateHolderNamespace}.LogStateHolder<{lm.RegularParameters[0].Type}>" + + return $"new global::{StateHolderNamespace}.LogValues<{lm.RegularParameters[0].Type}>" + $"({formatFunc}, \"{originalFormat}\", \"{NormalizeArgumentName(lm.RegularParameters[0].Name)}\", {lm.RegularParameters[0].Name})"; } @@ -421,7 +421,7 @@ private string GenHolder(LoggerMethod lm, string formatFunc) { if (lm.RegularParameters.Count > MaxStateHolderArity) { - _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolderN({formatFunc}, \"{originalFormat}\", new global::System.Collections.Generic.KeyValuePair[] {{ "); + _ = sb.Append($"new global::{StateHolderNamespace}.LogValuesN({formatFunc}, \"{originalFormat}\", new global::System.Collections.Generic.KeyValuePair[] {{ "); foreach (var p in lm.RegularParameters) { _ = sb.Append($"new (\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); @@ -444,7 +444,7 @@ private string GenHolder(LoggerMethod lm, string formatFunc) var tp = sb.ToString(); _ = sb.Clear(); - _ = sb.Append($"new global::{StateHolderNamespace}.LogStateHolder<{tp}>({formatFunc}, \"{originalFormat}\", _names{lm.Name}, "); + _ = sb.Append($"new global::{StateHolderNamespace}.LogValues<{tp}>({formatFunc}, \"{originalFormat}\", _names{lm.Name}, "); foreach (var p in lm.RegularParameters) { if (p != lm.RegularParameters[0]) From 60fef628dd9c636c371d460e609c973d98d633d7 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 16 Mar 2021 16:41:13 -0700 Subject: [PATCH 069/121] Improve test coverage --- .../gen/LoggerMessageGenerator.Emitter.cs | 6 +- .../gen/LoggerMessageGenerator.Parser.cs | 53 ++++---- .../LoggerMessageGeneratorEmitterTests.cs | 41 ++++--- .../LoggerMessageGeneratorParserTests.cs | 113 ++++++++++++++---- .../TestClasses/CollectionTestExtensions.cs | 2 - .../TestClasses/EnumerableTestExtensions.cs | 52 ++++++++ 6 files changed, 192 insertions(+), 75 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index cf9ac02154b2d..0205079a1feac 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -116,7 +116,7 @@ private string GenFormatFunc(LoggerMethod lm) { return string.Empty; } - + var sb = GetStringBuilder(); try { @@ -180,7 +180,7 @@ private string GenFormatFunc(LoggerMethod lm) { if (lm.RegularParameters[index].IsEnumerable) { - _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate(_holder[{index}].Value);\n"); + _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate((global::System.Collections.IEnumerable ?)_holder[{index}].Value);\n"); } else { @@ -191,7 +191,7 @@ private string GenFormatFunc(LoggerMethod lm) { if (lm.RegularParameters[index].IsEnumerable) { - _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate(_holder.Value{index + 1});\n"); + _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate((global::System.Collections.IEnumerable ?)_holder.Value{index + 1});\n"); } else { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 510322ab71f3e..4a75f34a32013 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -32,57 +32,56 @@ public IReadOnlyList GetLogClasses(IEnumerable(); + var loggerMessageAttribute = _compilation.GetTypeByMetadataName(LoggerMessageAttribute); + if (loggerMessageAttribute is null) + { + // nothing to do if this type isn't available + return Array.Empty(); + } + + var loggerSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILogger"); + if (loggerSymbol == null) + { + // nothing to do if this type isn't available + return Array.Empty(); + } + + var logLevelSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LogLevel"); + if (logLevelSymbol == null) + { + // nothing to do if this type isn't available + return Array.Empty(); + } var exceptionSymbol = _compilation.GetTypeByMetadataName("System.Exception"); if (exceptionSymbol == null) { Diag(DiagDescriptors.ErrorMissingRequiredType, null, "System.Exception"); - return results; + return Array.Empty(); } var enumerableSymbol = _compilation.GetTypeByMetadataName("System.Collections.IEnumerable"); if (enumerableSymbol == null) { Diag(DiagDescriptors.ErrorMissingRequiredType, null, "System.Collections.IEnumerable"); - return results; + return Array.Empty(); } var stringSymbol = _compilation.GetTypeByMetadataName("System.String"); if (stringSymbol == null) { Diag(DiagDescriptors.ErrorMissingRequiredType, null, "System.String"); - return results; + return Array.Empty(); } var dateTimeSymbol = _compilation.GetTypeByMetadataName("System.DateTime"); if (dateTimeSymbol == null) { Diag(DiagDescriptors.ErrorMissingRequiredType, null, "System.DateTime"); - return results; - } - - var loggerMessageAttribute = _compilation.GetTypeByMetadataName(LoggerMessageAttribute); - if (loggerMessageAttribute is null) - { - // nothing to do if this type isn't available - return results; - } - - var loggerSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILogger"); - if (loggerSymbol == null) - { - // nothing to do if this type isn't available - return results; - } - - var logLevelSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LogLevel"); - if (logLevelSymbol == null) - { - // nothing to do if this type isn't available - return results; + return Array.Empty(); } + var results = new List(); var ids = new HashSet(); // we enumerate by syntax tree, to minimize the need to instantiate semantic models (since they're expensive) @@ -232,7 +231,7 @@ public IReadOnlyList GetLogClasses(IEnumerable p0); + + [LoggerMessage(2, LogLevel.Error, "M2{p0}{p1}")] + public static partial void M2(ILogger logger, int p0, IEnumerable p1); + + [LoggerMessage(3, LogLevel.Error, "M3{p0}{p1}{p2}")] + public static partial void M3(ILogger logger, int p0, IEnumerable p1, int p2); + + [LoggerMessage(4, LogLevel.Error, "M4{p0}{p1}{p2}{p3}")] + public static partial void M4(ILogger logger, int p0, IEnumerable p1, int p2, int p3); + + [LoggerMessage(5, LogLevel.Error, "M5{p0}{p1}{p2}{p3}{p4}")] + public static partial void M5(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4); + + [LoggerMessage(6, LogLevel.Error, "M6{p0}{p1}{p2}{p3}{p4}{p5}")] + public static partial void M6(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5); + +#pragma warning disable S107 // Methods should not have too many parameters + + [LoggerMessage(7, LogLevel.Error, "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] + public static partial void M7(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6); + + [LoggerMessage(8, LogLevel.Error, "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] + public static partial void M8(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6, int p7); + + [LoggerMessage(9, LogLevel.Error, "M9{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}{p8}")] + public static partial void M9(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8); + } +} From 79dc2d4197a5a3f2aef730851b407ce63dac2f13 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 16 Mar 2021 21:13:02 -0700 Subject: [PATCH 070/121] Use the Invariant culture when formatting log messages --- .../gen/LoggerMessageGenerator.Emitter.cs | 43 ++++++++++--------- .../gen/LoggerMessageGenerator.Parser.cs | 3 +- .../gen/LoggerMessageGenerator.cs | 2 +- .../gen/Resources.Designer.cs | 6 +-- .../gen/Resources.resx | 4 +- .../LoggerMessageGeneratorEmitterTests.cs | 4 +- .../LoggerMessageGeneratorParserTests.cs | 36 ++++++++++------ .../TestClasses/EnumerableTestExtensions.cs | 10 +---- 8 files changed, 59 insertions(+), 49 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 0205079a1feac..cbf62f3444efd 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Text; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.Extensions.Logging.Generators { @@ -13,9 +12,9 @@ internal class Emitter { // The maximum arity of the LogValues-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation // for the array and boxing of all logging method arguments. - private const int MaxStateHolderArity = 6; - private const int MinStateHolderWithNameArray = 2; - private const string StateHolderNamespace = "Microsoft.Extensions.Logging.Internal"; + private const int MaxLogValuesArity = 6; + private const int MinLogValuesWithNameArray = 2; + private const string InternalNamespace = "Microsoft.Extensions.Logging.Internal"; private readonly Stack _builders = new (); private readonly bool _pascalCaseArguments; @@ -123,15 +122,15 @@ private string GenFormatFunc(LoggerMethod lm) string typeName; if (lm.RegularParameters.Count == 1) { - typeName = $"global::{StateHolderNamespace}.LogValues<{lm.RegularParameters[0].Type}>"; + typeName = $"global::{InternalNamespace}.LogValues<{lm.RegularParameters[0].Type}>"; } - else if (lm.RegularParameters.Count > MaxStateHolderArity) + else if (lm.RegularParameters.Count > MaxLogValuesArity) { - typeName = $"global::{StateHolderNamespace}.LogValuesN"; + typeName = $"global::{InternalNamespace}.LogValuesN"; } else { - _ = sb.Append($"global::{StateHolderNamespace}.LogValues<"); + _ = sb.Append($"global::{InternalNamespace}.LogValues<"); foreach (var p in lm.RegularParameters) { @@ -154,7 +153,7 @@ private string GenFormatFunc(LoggerMethod lm) { if (lm.RegularParameters[0].IsEnumerable) { - _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate(_holder.Value);\n"); + _ = sb.Append($" var {t} = global::{InternalNamespace}.ArgumentFormatter.Enumerate(_holder.Value);\n"); } else { @@ -174,13 +173,15 @@ private string GenFormatFunc(LoggerMethod lm) index++; } - if (index < lm.RegularParameters.Count) // can happen in some cases of malformed input + // check for an index that's too big, this can happen in some cases of malformed input + if (index < lm.RegularParameters.Count) { - if (lm.RegularParameters.Count > MaxStateHolderArity) + if (lm.RegularParameters.Count > MaxLogValuesArity) { if (lm.RegularParameters[index].IsEnumerable) { - _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate((global::System.Collections.IEnumerable ?)_holder[{index}].Value);\n"); + _ = sb.Append($" var {t} = " + + $"global::{InternalNamespace}.ArgumentFormatter.Enumerate((global::System.Collections.IEnumerable ?)_holder[{index}].Value);\n"); } else { @@ -191,7 +192,8 @@ private string GenFormatFunc(LoggerMethod lm) { if (lm.RegularParameters[index].IsEnumerable) { - _ = sb.Append($" var {t} = global::Microsoft.Extensions.Logging.Internal.ArgumentFormatter.Enumerate((global::System.Collections.IEnumerable ?)_holder.Value{index + 1});\n"); + _ = sb.Append($" var {t} = " + + $"global::{InternalNamespace}.ArgumentFormatter.Enumerate((global::System.Collections.IEnumerable ?)_holder.Value{index + 1});\n"); } else { @@ -207,7 +209,8 @@ private string GenFormatFunc(LoggerMethod lm) private static readonly global::System.Func<{typeName}, global::System.Exception?, string> _format{lm.Name} = (_holder, _) => {{ {sb} - return $""{EscapeMessageString(lm.Message!)}""; + global::System.FormattableString fs = $""{EscapeMessageString(lm.Message!)}""; + return global::System.FormattableString.Invariant(fs); }}; "; } @@ -219,7 +222,7 @@ private string GenFormatFunc(LoggerMethod lm) private string GenNameArray(LoggerMethod lm) { - if (lm.RegularParameters.Count is < MinStateHolderWithNameArray or > MaxStateHolderArity) + if (lm.RegularParameters.Count is < MinLogValuesWithNameArray or > MaxLogValuesArity) { return string.Empty; } @@ -407,21 +410,21 @@ private string GenHolder(LoggerMethod lm, string formatFunc) if (lm.RegularParameters.Count == 0) { - return $"new global::{StateHolderNamespace}.LogValues({formatFunc}, \"{originalFormat}\")"; + return $"new global::{InternalNamespace}.LogValues({formatFunc}, \"{originalFormat}\")"; } if (lm.RegularParameters.Count == 1) { - return $"new global::{StateHolderNamespace}.LogValues<{lm.RegularParameters[0].Type}>" + + return $"new global::{InternalNamespace}.LogValues<{lm.RegularParameters[0].Type}>" + $"({formatFunc}, \"{originalFormat}\", \"{NormalizeArgumentName(lm.RegularParameters[0].Name)}\", {lm.RegularParameters[0].Name})"; } var sb = GetStringBuilder(); try { - if (lm.RegularParameters.Count > MaxStateHolderArity) + if (lm.RegularParameters.Count > MaxLogValuesArity) { - _ = sb.Append($"new global::{StateHolderNamespace}.LogValuesN({formatFunc}, \"{originalFormat}\", new global::System.Collections.Generic.KeyValuePair[] {{ "); + _ = sb.Append($"new global::{InternalNamespace}.LogValuesN({formatFunc}, \"{originalFormat}\", new global::System.Collections.Generic.KeyValuePair[] {{ "); foreach (var p in lm.RegularParameters) { _ = sb.Append($"new (\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); @@ -444,7 +447,7 @@ private string GenHolder(LoggerMethod lm, string formatFunc) var tp = sb.ToString(); _ = sb.Clear(); - _ = sb.Append($"new global::{StateHolderNamespace}.LogValues<{tp}>({formatFunc}, \"{originalFormat}\", _names{lm.Name}, "); + _ = sb.Append($"new global::{InternalNamespace}.LogValues<{tp}>({formatFunc}, \"{originalFormat}\", _names{lm.Name}, "); foreach (var p in lm.RegularParameters) { if (p != lm.RegularParameters[0]) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 4a75f34a32013..e139d7b0ce7c9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -231,7 +231,8 @@ public IReadOnlyList GetLogClasses(IEnumerable - /// Looks up a localized string similar to Multiple logging messages are using event id {0}. + /// Looks up a localized string similar to Multiple logging methods are using event id {0}. /// internal static string ErrorEventIdReuseMessage { get { @@ -142,7 +142,7 @@ internal static string ErrorEventIdReuseMessage { } /// - /// Looks up a localized string similar to Multiple logging messages cannot use the same event id. + /// Looks up a localized string similar to Multiple logging methods cannot use the same event id. /// internal static string ErrorEventIdReuseTitle { get { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index b171486f93cb4..2b37d9e3d6a50 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -142,10 +142,10 @@ Could not find definition for type {0} - Multiple logging messages cannot use the same event id + Multiple logging methods cannot use the same event id - Multiple logging messages are using event id {0} + Multiple logging methods are using event id {0} Logging methods must return void diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index e427ed1bfe661..c5e061160647b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -68,12 +68,14 @@ public async Task TestEmitter(string response) }; foreach (var src in sources) - { + { var testSourceCode = await File.ReadAllTextAsync(src); var (d, r) = await RoslynTestUtils.RunGenerator( new LoggerMessageGenerator(), +#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly new[] { Assembly.GetAssembly(typeof(ILogger))!, Assembly.GetAssembly(typeof(LoggerMessageAttribute))! }, +#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly new[] { testSourceCode }, optionsProvider: new OptionsProvider(response)).ConfigureAwait(false); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 430a90824a878..e9d338856e660 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -218,9 +218,12 @@ public interface IEnumerable {} namespace Microsoft.Extensions.Logging { public enum LogLevel {} - public class LoggerMessageAttribute {} public interface ILogger {} } + namespace Microsoft.Extensions.Logging + { + public class LoggerMessageAttribute {} + } partial class C { } @@ -248,9 +251,12 @@ public interface IEnumerable {} namespace Microsoft.Extensions.Logging { public enum LogLevel {} - public class LoggerMessageAttribute {} public interface ILogger {} } + namespace Microsoft.Extensions.Logging + { + public class LoggerMessageAttribute {} + } partial class C { } @@ -278,9 +284,12 @@ public interface IEnumerable {} namespace Microsoft.Extensions.Logging { public enum LogLevel {} - public class LoggerMessageAttribute {} public interface ILogger {} } + namespace Microsoft.Extensions.Logging + { + public class LoggerMessageAttribute {} + } partial class C { } @@ -305,10 +314,13 @@ public class String {} namespace Microsoft.Extensions.Logging { public enum LogLevel {} - public class LoggerMessageAttribute {} public interface ILogger {} } - partial class C + namespace Microsoft.Extensions.Logging + { + public class LoggerMessageAttribute {} + } +R9 partial class C { } ", false, includeBaseReferences: false, includeLoggingReferences: false); @@ -380,7 +392,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0005", d[0].Id); + Assert.Equal(DiagDescriptors.ErrorEventIdReuse.Id, d[0].Id); } [Fact] @@ -397,7 +409,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0006", d[0].Id); + Assert.Equal(DiagDescriptors.ErrorInvalidMethodReturnType.Id, d[0].Id); } [Fact] @@ -412,7 +424,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0007", d[0].Id); + Assert.Equal(DiagDescriptors.ErrorMissingLogger.Id, d[0].Id); } [Fact] @@ -427,7 +439,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0008", d[0].Id); + Assert.Equal(DiagDescriptors.ErrorNotStaticMethod.Id, d[0].Id); } [Fact] @@ -442,8 +454,8 @@ static void M1(ILogger logger) {} "); Assert.Equal(2, d.Count); - Assert.Equal("LG0009", d[0].Id); - Assert.Equal("LG0016", d[1].Id); + Assert.Equal(DiagDescriptors.ErrorNotPartialMethod.Id, d[0].Id); + Assert.Equal(DiagDescriptors.ErrorMethodHasBody.Id, d[1].Id); } [Fact] @@ -458,7 +470,7 @@ partial class C "); Assert.Single(d); - Assert.Equal("LG0010", d[0].Id); + Assert.Equal(DiagDescriptors.ErrorMethodIsGeneric.Id, d[0].Id); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs index a025b42706711..2fbe197c5cf43 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs @@ -7,14 +7,6 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { - public class Foo : IEnumerable - { - public IEnumerator GetEnumerator() - { - return null!; - } - } - internal static partial class EnumerableTestExtensions { [LoggerMessage(0, LogLevel.Error, "M0")] @@ -47,6 +39,6 @@ internal static partial class EnumerableTestExtensions public static partial void M8(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6, int p7); [LoggerMessage(9, LogLevel.Error, "M9{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}{p8}")] - public static partial void M9(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8); + public static partial void M9(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8); } } From 055cf3ffd44749284a9236191e79b15f17343cf5 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 27 Mar 2021 06:47:17 -0700 Subject: [PATCH 071/121] Improve the error message around id reuse --- .../gen/LoggerMessageGenerator.Parser.cs | 2 +- .../gen/Resources.Designer.cs | 4 +- .../gen/Resources.resx | 4 +- .../LoggerMessageGeneratorParserTests.cs | 45 ++++++++++--------- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index e139d7b0ce7c9..515da4ccdc4d9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -190,7 +190,7 @@ public IReadOnlyList GetLogClasses(IEnumerable - /// Looks up a localized string similar to Multiple logging methods are using event id {0}. + /// Looks up a localized string similar to Multiple logging methods are using event id {0} in class {1}. /// internal static string ErrorEventIdReuseMessage { get { @@ -142,7 +142,7 @@ internal static string ErrorEventIdReuseMessage { } /// - /// Looks up a localized string similar to Multiple logging methods cannot use the same event id. + /// Looks up a localized string similar to Multiple logging methods cannot use the same event id within a class. /// internal static string ErrorEventIdReuseTitle { get { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index 2b37d9e3d6a50..9311dcbfe6edd 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -142,10 +142,10 @@ Could not find definition for type {0} - Multiple logging methods cannot use the same event id + Multiple logging methods cannot use the same event id within a class - Multiple logging methods are using event id {0} + Multiple logging methods are using event id {0} in class {1} Logging methods must return void diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index e9d338856e660..90a4e84dba910 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -24,7 +24,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorInvalidMethodName.Id, d[0].Id); } @@ -39,7 +39,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorMissingLogLevel.Id, d[0].Id); } @@ -58,7 +58,7 @@ static partial void M1(ILogger logger) } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorMethodHasBody.Id, d[0].Id); } @@ -73,7 +73,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ArgumentHasNoCorrespondingTemplate.Id, d[0].Id); } @@ -88,7 +88,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.TemplateHasNoCorrespondingArgument.Id, d[0].Id); } @@ -103,7 +103,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.RedundantQualifierInMessage.Id, d[0].Id); } @@ -118,7 +118,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.DontMentionExceptionInMessage.Id, d[0].Id); } @@ -133,7 +133,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.DontMentionLogLevelInMessage.Id, d[0].Id); } @@ -148,7 +148,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.DontMentionLoggerInMessage.Id, d[0].Id); } @@ -163,7 +163,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorInvalidParameterName.Id, d[0].Id); } @@ -178,7 +178,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.PassingDateTime.Id, d[0].Id); } @@ -196,7 +196,7 @@ public partial class Nested } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorNestedType.Id, d[0].Id); } @@ -229,7 +229,7 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorMissingRequiredType.Id, d[0].Id); } @@ -262,7 +262,7 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorMissingRequiredType.Id, d[0].Id); } @@ -295,7 +295,7 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorMissingRequiredType.Id, d[0].Id); } @@ -325,7 +325,7 @@ R9 partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorMissingRequiredType.Id, d[0].Id); } @@ -381,7 +381,7 @@ partial class C public async Task EventIdReuse() { var d = await RunGenerator(@" - partial class C + partial class MyClass { [LoggerMessage(0, LogLevel.Debug, ""M1"")] static partial void M1(ILogger logger); @@ -391,8 +391,9 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorEventIdReuse.Id, d[0].Id); + Assert.Contains("in class MyClass", d[0].GetMessage(), StringComparison.InvariantCulture); } [Fact] @@ -408,7 +409,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorInvalidMethodReturnType.Id, d[0].Id); } @@ -423,7 +424,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorMissingLogger.Id, d[0].Id); } @@ -438,7 +439,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorNotStaticMethod.Id, d[0].Id); } @@ -469,7 +470,7 @@ partial class C } "); - Assert.Single(d); + _ = Assert.Single(d); Assert.Equal(DiagDescriptors.ErrorMethodIsGeneric.Id, d[0].Id); } From abb50108a0ef08b28f5fda7b40ee591c7343c17c Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 27 Mar 2021 14:42:09 -0700 Subject: [PATCH 072/121] A few improvements - For instance-mode logging method, the code now binds to any single field that implements ILogger. The code generates an error if there are multiple matching fields. The code doesn't currently look up the inheritance hierarchy for fields or properties that match, it certainly could in the future if that is deemed useful. - Remove the global option to control the field name used for instance methods, since the field is now determined dynamically based on type. - Made a few diagnostics into warnings since they were really just advisory in nature. - Cleaned up the diagnostic and resource string names for better consistency. --- .../gen/DiagDescriptors.cs | 106 ++++---- .../gen/LoggerMessageGenerator.Emitter.cs | 14 +- .../gen/LoggerMessageGenerator.Parser.cs | 105 ++++++-- .../gen/LoggerMessageGenerator.cs | 8 +- .../gen/Resources.Designer.cs | 228 ++++++++++-------- .../gen/Resources.resx | 68 +++--- .../LoggerMessageGeneratorParserTests.cs | 69 ++++-- .../TestClasses/TestInstances.cs | 6 +- 8 files changed, 374 insertions(+), 230 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs index e1b087b9bfcd9..34eb33d8bb82d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs @@ -6,90 +6,90 @@ namespace Microsoft.Extensions.Logging.Generators { internal static class DiagDescriptors { - public static DiagnosticDescriptor ErrorInvalidMethodName { get; } = new ( + public static DiagnosticDescriptor InvalidLoggingMethodName { get; } = new ( id: "LG0000", - title: Resources.ErrorInvalidMethodNameTitle, - messageFormat: Resources.ErrorInvalidMethodNameMessage, + title: Resources.InvalidLoggingMethodNameTitle, + messageFormat: Resources.InvalidLoggingMethodNameMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor DontMentionLogLevelInMessage { get; } = new ( + public static DiagnosticDescriptor ShouldntMentionLogLevelInMessage { get; } = new ( id: "LG0001", - title: Resources.DontMentionLogLevelInMessageTitle, - messageFormat: Resources.DontMentionLogLevelInMessageMessage, + title: Resources.ShouldntMentionLogLevelInMessageTitle, + messageFormat: Resources.ShouldntMentionLogLevelInMessageMessage, category: "LoggingGenerator", - DiagnosticSeverity.Error, + DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorInvalidParameterName { get; } = new ( + public static DiagnosticDescriptor InvalidLoggingMethodParameterName { get; } = new ( id: "LG0002", - title: Resources.ErrorInvalidParameterNameTitle, - messageFormat: Resources.ErrorInvalidParameterNameMessage, + title: Resources.InvalidLoggingMethodParameterNameTitle, + messageFormat: Resources.InvalidLoggingMethodParameterNameMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorNestedType { get; } = new ( + public static DiagnosticDescriptor LoggingMethodInNestedType { get; } = new ( id: "LG0003", - title: Resources.ErrorNestedTypeTitle, - messageFormat: Resources.ErrorNestedTypeMessage, + title: Resources.LoggingMethodInNestedTypeTitle, + messageFormat: Resources.LoggingMethodInNestedTypeMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorMissingRequiredType { get; } = new ( + public static DiagnosticDescriptor MissingRequiredType { get; } = new ( id: "LG0004", - title: Resources.ErrorMissingRequiredTypeTitle, - messageFormat: Resources.ErrorMissingRequiredTypeMessage, + title: Resources.MissingRequiredTypeTitle, + messageFormat: Resources.MissingRequiredTypeMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorEventIdReuse { get; } = new ( + public static DiagnosticDescriptor ShouldntReuseEventIds { get; } = new ( id: "LG0005", - title: Resources.ErrorEventIdReuseTitle, - messageFormat: Resources.ErrorEventIdReuseMessage, + title: Resources.ShouldntReuseEventIdsTitle, + messageFormat: Resources.ShouldntReuseEventIdsMessage, category: "LoggingGenerator", - DiagnosticSeverity.Error, + DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorInvalidMethodReturnType { get; } = new ( + public static DiagnosticDescriptor LoggingMethodMustReturnVoid { get; } = new ( id: "LG0006", - title: Resources.ErrorInvalidMethodReturnTypeTitle, - messageFormat: Resources.ErrorInvalidMethodReturnTypeMessage, + title: Resources.LoggingMethodMustReturnVoidTitle, + messageFormat: Resources.LoggingMethodMustReturnVoidMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorMissingLogger { get; } = new ( + public static DiagnosticDescriptor MissingLoggerArgument { get; } = new ( id: "LG0007", - title: Resources.ErrorMissingLoggerTitle, - messageFormat: Resources.ErrorMissingLoggerMessage, + title: Resources.MissingLoggerArgumentTitle, + messageFormat: Resources.MissingLoggerArgumentMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorNotStaticMethod { get; } = new ( + public static DiagnosticDescriptor LoggingMethodShouldBeStatic { get; } = new ( id: "LG0008", - title: Resources.ErrorNotStaticMethodTitle, - messageFormat: Resources.ErrorNotStaticMethodMessage, + title: Resources.LoggingMethodShouldBeStaticTitle, + messageFormat: Resources.LoggingMethodShouldBeStaticMessage, category: "LoggingGenerator", - DiagnosticSeverity.Error, + DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorNotPartialMethod { get; } = new ( + public static DiagnosticDescriptor LoggingMethodMustBePartial { get; } = new ( id: "LG0009", - title: Resources.ErrorNotPartialMethodTitle, - messageFormat: Resources.ErrorNotPartialMethodMessage, + title: Resources.LoggingMethodMustBePartialTitle, + messageFormat: Resources.LoggingMethodMustBePartialMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorMethodIsGeneric { get; } = new ( + public static DiagnosticDescriptor LoggingMethodIsGeneric { get; } = new ( id: "LG0010", - title: Resources.ErrorMethodIsGenericTitle, - messageFormat: Resources.ErrorMethodIsGenericMessage, + title: Resources.LoggingMethodIsGenericTitle, + messageFormat: Resources.LoggingMethodIsGenericMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -110,10 +110,10 @@ internal static class DiagDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor DontMentionExceptionInMessage { get; } = new ( + public static DiagnosticDescriptor ShouldntMentionExceptionInMessage { get; } = new ( id: "LG0013", - title: Resources.DontMentionExceptionInMessageTitle, - messageFormat: Resources.DontMentionExceptionInMessageMessage, + title: Resources.ShouldntMentionExceptionInMessageTitle, + messageFormat: Resources.ShouldntMentionExceptionInMessageMessage, category: "LoggingGenerator", DiagnosticSeverity.Warning, isEnabledByDefault: true); @@ -134,18 +134,18 @@ internal static class DiagDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorMethodHasBody { get; } = new ( + public static DiagnosticDescriptor LoggingMethodHasBody { get; } = new ( id: "LG0016", - title: Resources.ErrorMethodHasBodyTitle, - messageFormat: Resources.ErrorMethodHasBodyMessage, + title: Resources.LoggingMethodHasBodyTitle, + messageFormat: Resources.LoggingMethodHasBodyMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ErrorMissingLogLevel { get; } = new ( + public static DiagnosticDescriptor MissingLogLevel { get; } = new ( id: "LG0017", - title: Resources.ErrorMissingLogLevelTitle, - messageFormat: Resources.ErrorMissingLogLevelMessage, + title: Resources.MissingLogLevelTitle, + messageFormat: Resources.MissingLogLevelMessage, category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -157,5 +157,21 @@ internal static class DiagDescriptors category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static DiagnosticDescriptor MissingLoggerField { get; } = new ( + id: "LG0019", + title: Resources.MissingLoggerFieldTitle, + messageFormat: Resources.MissingLoggerFieldMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor MultipleLoggerFields { get; } = new( + id: "LG0020", + title: Resources.MultipleLoggerFieldsTitle, + messageFormat: Resources.MultipleLoggerFieldsMessage, + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index cbf62f3444efd..6e167274f44ba 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -18,13 +18,11 @@ internal class Emitter private readonly Stack _builders = new (); private readonly bool _pascalCaseArguments; - private readonly string _fieldName; private readonly bool _emitDefaultMessage; - public Emitter(bool pascalCaseArguments, string fieldName = "_logger", bool emitDefaultMessage = true) + public Emitter(bool pascalCaseArguments, bool emitDefaultMessage = true) { _pascalCaseArguments = pascalCaseArguments; - _fieldName = fieldName; _emitDefaultMessage = emitDefaultMessage; } @@ -308,24 +306,23 @@ private string GenLogMethod(LoggerMethod lm) formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message!)}\""; } - string loggerArg = _fieldName; + string logger = lm.LoggerField; foreach (var p in lm.AllParameters) { if (p.IsLogger) { - loggerArg = p.Name; + logger = p.Name; break; } } -#pragma warning disable S103 // Lines should not be too long return $@" [global::System.Runtime.CompilerServices.CompilerGenerated] {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{GenParameters(lm)}) {{ - if ({loggerArg}.IsEnabled({level})) + if ({logger}.IsEnabled({level})) {{ - {loggerArg}.Log( + {logger}.Log( {level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), {GenHolder(lm, formatFunc)}, @@ -334,7 +331,6 @@ private string GenLogMethod(LoggerMethod lm) }} }} "; -#pragma warning restore S103 // Lines should not be too long } private void AutoGenerateMessage(LoggerMethod lm) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 515da4ccdc4d9..ba9090f74247f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -56,28 +56,28 @@ public IReadOnlyList GetLogClasses(IEnumerable(); } var enumerableSymbol = _compilation.GetTypeByMetadataName("System.Collections.IEnumerable"); if (enumerableSymbol == null) { - Diag(DiagDescriptors.ErrorMissingRequiredType, null, "System.Collections.IEnumerable"); + Diag(DiagDescriptors.MissingRequiredType, null, "System.Collections.IEnumerable"); return Array.Empty(); } var stringSymbol = _compilation.GetTypeByMetadataName("System.String"); if (stringSymbol == null) { - Diag(DiagDescriptors.ErrorMissingRequiredType, null, "System.String"); + Diag(DiagDescriptors.MissingRequiredType, null, "System.String"); return Array.Empty(); } var dateTimeSymbol = _compilation.GetTypeByMetadataName("System.DateTime"); if (dateTimeSymbol == null) { - Diag(DiagDescriptors.ErrorMissingRequiredType, null, "System.DateTime"); + Diag(DiagDescriptors.MissingRequiredType, null, "System.DateTime"); return Array.Empty(); } @@ -88,16 +88,18 @@ public IReadOnlyList GetLogClasses(IEnumerable x.SyntaxTree)) { SemanticModel? sm = null; - foreach (var classDef in group) + foreach (var classDec in group) { // stop if we're asked to _cancellationToken.ThrowIfCancellationRequested(); LoggerClass? lc = null; string nspace = string.Empty; + string? loggerField = null; + bool multipleLoggerFields = false; ids.Clear(); - foreach (var member in classDef.Members) + foreach (var member in classDec.Members) { var method = member as MethodDeclarationSyntax; if (method == null) @@ -110,7 +112,7 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable 0) { // we don't currently support generic methods - Diag(DiagDescriptors.ErrorMethodIsGeneric, method.Identifier.GetLocation()); + Diag(DiagDescriptors.LoggingMethodIsGeneric, method.Identifier.GetLocation()); keepMethod = false; } @@ -177,20 +179,20 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs index a6090492d9b61..bd075ba8b5885 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs @@ -33,7 +33,6 @@ public void Execute(GeneratorExecutionContext context) } var pascalCaseArguments = false; - var fieldName = "_logger"; var emitDefaultMessage = true; if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("PascalCaseArguments", out var pca)) @@ -41,18 +40,13 @@ public void Execute(GeneratorExecutionContext context) pascalCaseArguments = (pca.ToUpperInvariant() == "TRUE") || (pca.ToUpperInvariant() == "YES"); } - if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("FieldName", out var fn)) - { - fieldName = fn; - } - if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("EmitDefaultMessage", out var edm)) { emitDefaultMessage = (edm.ToUpperInvariant() == "TRUE") || (edm.ToUpperInvariant() == "YES"); } var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); - var e = new Emitter(pascalCaseArguments, fieldName, emitDefaultMessage); + var e = new Emitter(pascalCaseArguments, emitDefaultMessage); var logClasses = p.GetLogClasses(receiver.ClassDeclarations); var result = e.Emit(logClasses, context.CancellationToken); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs index 0f45642ba5731..31956abac02fb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs @@ -78,24 +78,6 @@ internal static string ArgumentHasNoCorrespondingTemplateTitle { } } - /// - /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care. - /// - internal static string DontMentionExceptionInMessageMessage { - get { - return ResourceManager.GetString("DontMentionExceptionInMessageMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include exception parameters as templates in the logging message. - /// - internal static string DontMentionExceptionInMessageTitle { - get { - return ResourceManager.GetString("DontMentionExceptionInMessageTitle", resourceCulture); - } - } - /// /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. /// @@ -115,236 +97,236 @@ internal static string DontMentionLoggerInMessageTitle { } /// - /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. + /// Looks up a localized string similar to Logging method names cannot start with _. /// - internal static string DontMentionLogLevelInMessageMessage { + internal static string InvalidLoggingMethodNameMessage { get { - return ResourceManager.GetString("DontMentionLogLevelInMessageMessage", resourceCulture); + return ResourceManager.GetString("InvalidLoggingMethodNameMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Don't include log level parameters as templates in the logging message. + /// Looks up a localized string similar to Logging method names cannot start with _. /// - internal static string DontMentionLogLevelInMessageTitle { + internal static string InvalidLoggingMethodNameTitle { get { - return ResourceManager.GetString("DontMentionLogLevelInMessageTitle", resourceCulture); + return ResourceManager.GetString("InvalidLoggingMethodNameTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Multiple logging methods are using event id {0} in class {1}. + /// Looks up a localized string similar to Logging method parameter names cannot start with _. /// - internal static string ErrorEventIdReuseMessage { + internal static string InvalidLoggingMethodParameterNameMessage { get { - return ResourceManager.GetString("ErrorEventIdReuseMessage", resourceCulture); + return ResourceManager.GetString("InvalidLoggingMethodParameterNameMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Multiple logging methods cannot use the same event id within a class. + /// Looks up a localized string similar to Logging method parameter names cannot start with _. /// - internal static string ErrorEventIdReuseTitle { + internal static string InvalidLoggingMethodParameterNameTitle { get { - return ResourceManager.GetString("ErrorEventIdReuseTitle", resourceCulture); + return ResourceManager.GetString("InvalidLoggingMethodParameterNameTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Logging method names cannot start with _. + /// Looks up a localized string similar to Logging methods cannot have a body. /// - internal static string ErrorInvalidMethodNameMessage { + internal static string LoggingMethodHasBodyMessage { get { - return ResourceManager.GetString("ErrorInvalidMethodNameMessage", resourceCulture); + return ResourceManager.GetString("LoggingMethodHasBodyMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging method names cannot start with _. + /// Looks up a localized string similar to Logging methods cannot have a body. /// - internal static string ErrorInvalidMethodNameTitle { + internal static string LoggingMethodHasBodyTitle { get { - return ResourceManager.GetString("ErrorInvalidMethodNameTitle", resourceCulture); + return ResourceManager.GetString("LoggingMethodHasBodyTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods must return void. + /// Looks up a localized string similar to Logging class cannot be in nested types. /// - internal static string ErrorInvalidMethodReturnTypeMessage { + internal static string LoggingMethodInNestedTypeMessage { get { - return ResourceManager.GetString("ErrorInvalidMethodReturnTypeMessage", resourceCulture); + return ResourceManager.GetString("LoggingMethodInNestedTypeMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods must return void. + /// Looks up a localized string similar to Logging class cannot be in nested types. /// - internal static string ErrorInvalidMethodReturnTypeTitle { + internal static string LoggingMethodInNestedTypeTitle { get { - return ResourceManager.GetString("ErrorInvalidMethodReturnTypeTitle", resourceCulture); + return ResourceManager.GetString("LoggingMethodInNestedTypeTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Logging method parameter names cannot start with _. + /// Looks up a localized string similar to Logging methods cannot be generic. /// - internal static string ErrorInvalidParameterNameMessage { + internal static string LoggingMethodIsGenericMessage { get { - return ResourceManager.GetString("ErrorInvalidParameterNameMessage", resourceCulture); + return ResourceManager.GetString("LoggingMethodIsGenericMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging method parameter names cannot start with _. + /// Looks up a localized string similar to Logging methods cannot be generic. /// - internal static string ErrorInvalidParameterNameTitle { + internal static string LoggingMethodIsGenericTitle { get { - return ResourceManager.GetString("ErrorInvalidParameterNameTitle", resourceCulture); + return ResourceManager.GetString("LoggingMethodIsGenericTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods cannot have a body. + /// Looks up a localized string similar to Logging methods must be partial. /// - internal static string ErrorMethodHasBodyMessage { + internal static string LoggingMethodMustBePartialMessage { get { - return ResourceManager.GetString("ErrorMethodHasBodyMessage", resourceCulture); + return ResourceManager.GetString("LoggingMethodMustBePartialMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods cannot have a body. + /// Looks up a localized string similar to Logging methods must be partial. /// - internal static string ErrorMethodHasBodyTitle { + internal static string LoggingMethodMustBePartialTitle { get { - return ResourceManager.GetString("ErrorMethodHasBodyTitle", resourceCulture); + return ResourceManager.GetString("LoggingMethodMustBePartialTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods cannot be generic. + /// Looks up a localized string similar to Logging methods must return void. /// - internal static string ErrorMethodIsGenericMessage { + internal static string LoggingMethodMustReturnVoidMessage { get { - return ResourceManager.GetString("ErrorMethodIsGenericMessage", resourceCulture); + return ResourceManager.GetString("LoggingMethodMustReturnVoidMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods cannot be generic. + /// Looks up a localized string similar to Logging methods must return void. /// - internal static string ErrorMethodIsGenericTitle { + internal static string LoggingMethodMustReturnVoidTitle { get { - return ResourceManager.GetString("ErrorMethodIsGenericTitle", resourceCulture); + return ResourceManager.GetString("LoggingMethodMustReturnVoidTitle", resourceCulture); } } /// - /// Looks up a localized string similar to One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. + /// Looks up a localized string similar to Logging methods must be static. /// - internal static string ErrorMissingLoggerMessage { + internal static string LoggingMethodShouldBeStaticMessage { get { - return ResourceManager.GetString("ErrorMissingLoggerMessage", resourceCulture); + return ResourceManager.GetString("LoggingMethodShouldBeStaticMessage", resourceCulture); } } /// - /// Looks up a localized string similar to One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. + /// Looks up a localized string similar to Logging methods must be static. /// - internal static string ErrorMissingLoggerTitle { + internal static string LoggingMethodShouldBeStaticTitle { get { - return ResourceManager.GetString("ErrorMissingLoggerTitle", resourceCulture); + return ResourceManager.GetString("LoggingMethodShouldBeStaticTitle", resourceCulture); } } /// - /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. + /// Looks up a localized string similar to One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. /// - internal static string ErrorMissingLogLevelMessage { + internal static string MissingLoggerArgumentMessage { get { - return ResourceManager.GetString("ErrorMissingLogLevelMessage", resourceCulture); + return ResourceManager.GetString("MissingLoggerArgumentMessage", resourceCulture); } } /// - /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. + /// Looks up a localized string similar to One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. /// - internal static string ErrorMissingLogLevelTitle { + internal static string MissingLoggerArgumentTitle { get { - return ResourceManager.GetString("ErrorMissingLogLevelTitle", resourceCulture); + return ResourceManager.GetString("MissingLoggerArgumentTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Could not find definition for type {0}. + /// Looks up a localized string similar to Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}. /// - internal static string ErrorMissingRequiredTypeMessage { + internal static string MissingLoggerFieldMessage { get { - return ResourceManager.GetString("ErrorMissingRequiredTypeMessage", resourceCulture); + return ResourceManager.GetString("MissingLoggerFieldMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Could not find a required type definition. + /// Looks up a localized string similar to Couldn't find a field of type Microsoft.Extensions.Logging.ILogger. /// - internal static string ErrorMissingRequiredTypeTitle { + internal static string MissingLoggerFieldTitle { get { - return ResourceManager.GetString("ErrorMissingRequiredTypeTitle", resourceCulture); + return ResourceManager.GetString("MissingLoggerFieldTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Logging class cannot be in nested types. + /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. /// - internal static string ErrorNestedTypeMessage { + internal static string MissingLogLevelMessage { get { - return ResourceManager.GetString("ErrorNestedTypeMessage", resourceCulture); + return ResourceManager.GetString("MissingLogLevelMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging class cannot be in nested types. + /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. /// - internal static string ErrorNestedTypeTitle { + internal static string MissingLogLevelTitle { get { - return ResourceManager.GetString("ErrorNestedTypeTitle", resourceCulture); + return ResourceManager.GetString("MissingLogLevelTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods must be partial. + /// Looks up a localized string similar to Could not find definition for type {0}. /// - internal static string ErrorNotPartialMethodMessage { + internal static string MissingRequiredTypeMessage { get { - return ResourceManager.GetString("ErrorNotPartialMethodMessage", resourceCulture); + return ResourceManager.GetString("MissingRequiredTypeMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods must be partial. + /// Looks up a localized string similar to Could not find a required type definition. /// - internal static string ErrorNotPartialMethodTitle { + internal static string MissingRequiredTypeTitle { get { - return ResourceManager.GetString("ErrorNotPartialMethodTitle", resourceCulture); + return ResourceManager.GetString("MissingRequiredTypeTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods must be static. + /// Looks up a localized string similar to Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}. /// - internal static string ErrorNotStaticMethodMessage { + internal static string MultipleLoggerFieldsMessage { get { - return ResourceManager.GetString("ErrorNotStaticMethodMessage", resourceCulture); + return ResourceManager.GetString("MultipleLoggerFieldsMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods must be static. + /// Looks up a localized string similar to Found multiple fields of type Microsoft.Extensions.Logging.ILogger. /// - internal static string ErrorNotStaticMethodTitle { + internal static string MultipleLoggerFieldsTitle { get { - return ResourceManager.GetString("ErrorNotStaticMethodTitle", resourceCulture); + return ResourceManager.GetString("MultipleLoggerFieldsTitle", resourceCulture); } } @@ -384,6 +366,60 @@ internal static string RedundantQualifierInMessageTitle { } } + /// + /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care. + /// + internal static string ShouldntMentionExceptionInMessageMessage { + get { + return ResourceManager.GetString("ShouldntMentionExceptionInMessageMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't include exception parameters as templates in the logging message. + /// + internal static string ShouldntMentionExceptionInMessageTitle { + get { + return ResourceManager.GetString("ShouldntMentionExceptionInMessageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. + /// + internal static string ShouldntMentionLogLevelInMessageMessage { + get { + return ResourceManager.GetString("ShouldntMentionLogLevelInMessageMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't include log level parameters as templates in the logging message. + /// + internal static string ShouldntMentionLogLevelInMessageTitle { + get { + return ResourceManager.GetString("ShouldntMentionLogLevelInMessageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Multiple logging methods are using event id {0} in class {1}. + /// + internal static string ShouldntReuseEventIdsMessage { + get { + return ResourceManager.GetString("ShouldntReuseEventIdsMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Multiple logging methods cannot use the same event id within a class. + /// + internal static string ShouldntReuseEventIdsTitle { + get { + return ResourceManager.GetString("ShouldntReuseEventIdsTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Template {0} is not provided as argument to the logging method. /// diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index 9311dcbfe6edd..ad7fac845d403 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -117,70 +117,70 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + Logging method names cannot start with _ - + Logging method names cannot start with _ - + Logging method parameter names cannot start with _ - + Logging method parameter names cannot start with _ - + Logging class cannot be in nested types - + Logging class cannot be in nested types - + Could not find a required type definition - + Could not find definition for type {0} - + Multiple logging methods cannot use the same event id within a class - + Multiple logging methods are using event id {0} in class {1} - + Logging methods must return void - + Logging methods must return void - + One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + Logging methods must be static - + Logging methods must be static - + Logging methods must be partial - + Logging methods must be partial - + Logging methods cannot be generic - + Logging methods cannot be generic - + Don't include a template for {0} in the logging message since it is implicitly taken care - + Don't include exception parameters as templates in the logging message @@ -207,22 +207,22 @@ Logging template has no corresponding method argument - + Logging methods cannot have a body - + Logging methods cannot have a body - + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - + Don't include a template for {0} in the logging message since it is implicitly taken care of - + Don't include log level parameters as templates in the logging message @@ -231,4 +231,16 @@ Don't include logger parameters as templates in the logging message + + Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + + + Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + + + Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + + + Found multiple fields of type Microsoft.Extensions.Logging.ILogger + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 90a4e84dba910..7e7c0bbebdcb4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -25,7 +25,7 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorInvalidMethodName.Id, d[0].Id); + Assert.Equal(DiagDescriptors.InvalidLoggingMethodName.Id, d[0].Id); } [Fact] @@ -40,7 +40,7 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorMissingLogLevel.Id, d[0].Id); + Assert.Equal(DiagDescriptors.MissingLogLevel.Id, d[0].Id); } [Fact] @@ -59,7 +59,7 @@ static partial void M1(ILogger logger) "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorMethodHasBody.Id, d[0].Id); + Assert.Equal(DiagDescriptors.LoggingMethodHasBody.Id, d[0].Id); } [Fact] @@ -119,7 +119,7 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.DontMentionExceptionInMessage.Id, d[0].Id); + Assert.Equal(DiagDescriptors.ShouldntMentionExceptionInMessage.Id, d[0].Id); } [Fact] @@ -134,7 +134,7 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.DontMentionLogLevelInMessage.Id, d[0].Id); + Assert.Equal(DiagDescriptors.ShouldntMentionLogLevelInMessage.Id, d[0].Id); } [Fact] @@ -164,7 +164,7 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorInvalidParameterName.Id, d[0].Id); + Assert.Equal(DiagDescriptors.InvalidLoggingMethodParameterName.Id, d[0].Id); } [Fact] @@ -197,7 +197,7 @@ public partial class Nested "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorNestedType.Id, d[0].Id); + Assert.Equal(DiagDescriptors.LoggingMethodInNestedType.Id, d[0].Id); } [Fact] @@ -230,7 +230,7 @@ partial class C ", false, includeBaseReferences: false, includeLoggingReferences: false); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorMissingRequiredType.Id, d[0].Id); + Assert.Equal(DiagDescriptors.MissingRequiredType.Id, d[0].Id); } [Fact] @@ -263,7 +263,7 @@ partial class C ", false, includeBaseReferences: false, includeLoggingReferences: false); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorMissingRequiredType.Id, d[0].Id); + Assert.Equal(DiagDescriptors.MissingRequiredType.Id, d[0].Id); } [Fact] @@ -296,7 +296,7 @@ partial class C ", false, includeBaseReferences: false, includeLoggingReferences: false); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorMissingRequiredType.Id, d[0].Id); + Assert.Equal(DiagDescriptors.MissingRequiredType.Id, d[0].Id); } [Fact] @@ -326,7 +326,7 @@ R9 partial class C ", false, includeBaseReferences: false, includeLoggingReferences: false); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorMissingRequiredType.Id, d[0].Id); + Assert.Equal(DiagDescriptors.MissingRequiredType.Id, d[0].Id); } [Fact] @@ -392,7 +392,7 @@ partial class MyClass "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorEventIdReuse.Id, d[0].Id); + Assert.Equal(DiagDescriptors.ShouldntReuseEventIds.Id, d[0].Id); Assert.Contains("in class MyClass", d[0].GetMessage(), StringComparison.InvariantCulture); } @@ -410,7 +410,7 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorInvalidMethodReturnType.Id, d[0].Id); + Assert.Equal(DiagDescriptors.LoggingMethodMustReturnVoid.Id, d[0].Id); } [Fact] @@ -425,7 +425,7 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorMissingLogger.Id, d[0].Id); + Assert.Equal(DiagDescriptors.MissingLoggerArgument.Id, d[0].Id); } [Fact] @@ -440,7 +440,40 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorNotStaticMethod.Id, d[0].Id); + Assert.Equal(DiagDescriptors.LoggingMethodShouldBeStatic.Id, d[0].Id); + } + + [Fact] + public async Task NoILoggerField() + { + var d = await RunGenerator(@" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public partial void M1(); + } + "); + + _ = Assert.Single(d); + Assert.Equal(DiagDescriptors.MissingLoggerField.Id, d[0].Id); + } + + [Fact] + public async Task MultipleILoggerFields() + { + var d = await RunGenerator(@" + partial class C + { + public ILogger _logger1; + public ILogger _logger2; + + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + public partial void M1(); + } + "); + + _ = Assert.Single(d); + Assert.Equal(DiagDescriptors.MultipleLoggerFields.Id, d[0].Id); } [Fact] @@ -455,8 +488,8 @@ static void M1(ILogger logger) {} "); Assert.Equal(2, d.Count); - Assert.Equal(DiagDescriptors.ErrorNotPartialMethod.Id, d[0].Id); - Assert.Equal(DiagDescriptors.ErrorMethodHasBody.Id, d[1].Id); + Assert.Equal(DiagDescriptors.LoggingMethodMustBePartial.Id, d[0].Id); + Assert.Equal(DiagDescriptors.LoggingMethodHasBody.Id, d[1].Id); } [Fact] @@ -471,7 +504,7 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.ErrorMethodIsGeneric.Id, d[0].Id); + Assert.Equal(DiagDescriptors.LoggingMethodIsGeneric.Id, d[0].Id); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs index 96fa79e91eab8..f711722672ea1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs @@ -5,13 +5,13 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { - internal partial class TestInstances + public partial class TestInstances { - private readonly ILogger _logger; + private readonly ILogger _myLogger; public TestInstances(ILogger logger) { - _logger = logger; + _myLogger = logger; } [LoggerMessage(0, LogLevel.Error, "M0")] From 9087427cd447034602ab992e5183fa40e21cb141 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 30 Mar 2021 05:49:33 -0700 Subject: [PATCH 073/121] Emit GenerateCodeAttribute instead of CompilerGeneratedAttribute --- .../gen/LoggerMessageGenerator.Emitter.cs | 8 +++++--- .../LoggerMessageGeneratedCodeTests.cs | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 6e167274f44ba..a61aa6ae13afe 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -1,6 +1,7 @@ // © Microsoft Corporation. All rights reserved. using System.Collections.Generic; +using System.Reflection; using System.Text; using System.Threading; @@ -15,6 +16,7 @@ internal class Emitter private const int MaxLogValuesArity = 6; private const int MinLogValuesWithNameArray = 2; private const string InternalNamespace = "Microsoft.Extensions.Logging.Internal"; + private readonly string GeneratedCodeAttribute = $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{Assembly.GetExecutingAssembly().GetName().Name}\", \"{Assembly.GetExecutingAssembly().GetName().Version}\")"; private readonly Stack _builders = new (); private readonly bool _pascalCaseArguments; @@ -203,7 +205,7 @@ private string GenFormatFunc(LoggerMethod lm) } return $@" - [global::System.Runtime.CompilerServices.CompilerGenerated] + [{GeneratedCodeAttribute}] private static readonly global::System.Func<{typeName}, global::System.Exception?, string> _format{lm.Name} = (_holder, _) => {{ {sb} @@ -228,7 +230,7 @@ private string GenNameArray(LoggerMethod lm) var sb = GetStringBuilder(); try { - _ = sb.Append("\n [global::System.Runtime.CompilerServices.CompilerGenerated]\n"); + _ = sb.Append($"\n [{GeneratedCodeAttribute}]\n"); _ = sb.Append($" private static readonly string[] _names{lm.Name} = new[] {{ "); foreach (var p in lm.RegularParameters) { @@ -317,7 +319,7 @@ private string GenLogMethod(LoggerMethod lm) } return $@" - [global::System.Runtime.CompilerServices.CompilerGenerated] + [{GeneratedCodeAttribute}] {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{GenParameters(lm)}) {{ if ({logger}.IsEnabled({level})) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 75a1c2cf18e20..402d0a2114a25 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Generators.Test.TestClasses; using Xunit; From 58b3369eb13bca2dcc3428d2aa0748fd8c8402fa Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Tue, 30 Mar 2021 18:26:19 -0700 Subject: [PATCH 074/121] Minor cleanup items --- .../gen/LoggerMessageGenerator.Emitter.cs | 11 +++++++---- .../gen/LoggerMessageGenerator.Parser.cs | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index a61aa6ae13afe..38c919089452b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -16,8 +16,11 @@ internal class Emitter private const int MaxLogValuesArity = 6; private const int MinLogValuesWithNameArray = 2; private const string InternalNamespace = "Microsoft.Extensions.Logging.Internal"; - private readonly string GeneratedCodeAttribute = $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{Assembly.GetExecutingAssembly().GetName().Name}\", \"{Assembly.GetExecutingAssembly().GetName().Version}\")"; + private readonly string _generatedCodeAttribute = + $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" + + $"\"{typeof(Emitter).Assembly.GetName().Name}\"," + + $"\"{typeof(Emitter).Assembly.GetName().Version}\")"; private readonly Stack _builders = new (); private readonly bool _pascalCaseArguments; private readonly bool _emitDefaultMessage; @@ -205,7 +208,7 @@ private string GenFormatFunc(LoggerMethod lm) } return $@" - [{GeneratedCodeAttribute}] + [{_generatedCodeAttribute}] private static readonly global::System.Func<{typeName}, global::System.Exception?, string> _format{lm.Name} = (_holder, _) => {{ {sb} @@ -230,7 +233,7 @@ private string GenNameArray(LoggerMethod lm) var sb = GetStringBuilder(); try { - _ = sb.Append($"\n [{GeneratedCodeAttribute}]\n"); + _ = sb.Append($"\n [{_generatedCodeAttribute}]\n"); _ = sb.Append($" private static readonly string[] _names{lm.Name} = new[] {{ "); foreach (var p in lm.RegularParameters) { @@ -319,7 +322,7 @@ private string GenLogMethod(LoggerMethod lm) } return $@" - [{GeneratedCodeAttribute}] + [{_generatedCodeAttribute}] {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{GenParameters(lm)}) {{ if ({logger}.IsEnabled({level})) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index ba9090f74247f..de6e855a49712 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -442,7 +442,7 @@ public IReadOnlyList GetLogClasses(IEnumerable Date: Tue, 30 Mar 2021 21:18:25 -0700 Subject: [PATCH 075/121] Added preliminary support for an alternate code gen strategy --- .../gen/LoggerMessageGenerator.Emitter.cs | 113 +++++++++++++++++- 1 file changed, 109 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 38c919089452b..479662658c2c6 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -1,7 +1,8 @@ // © Microsoft Corporation. All rights reserved. +//#define LOGGER_MESSAGE_DEFINE + using System.Collections.Generic; -using System.Reflection; using System.Text; using System.Threading; @@ -14,17 +15,19 @@ internal class Emitter // The maximum arity of the LogValues-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation // for the array and boxing of all logging method arguments. private const int MaxLogValuesArity = 6; +#if !LOGGER_MESSAGE_DEFINE private const int MinLogValuesWithNameArray = 2; +#endif private const string InternalNamespace = "Microsoft.Extensions.Logging.Internal"; private readonly string _generatedCodeAttribute = $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" + - $"\"{typeof(Emitter).Assembly.GetName().Name}\"," + + $"\"{typeof(Emitter).Assembly.GetName().Name}\", " + $"\"{typeof(Emitter).Assembly.GetName().Version}\")"; private readonly Stack _builders = new (); private readonly bool _pascalCaseArguments; private readonly bool _emitDefaultMessage; - + public Emitter(bool pascalCaseArguments, bool emitDefaultMessage = true) { _pascalCaseArguments = pascalCaseArguments; @@ -71,6 +74,7 @@ private string GenType(LoggerClass lc) AutoGenerateMessage(lm); } +#if !LOGGER_MESSAGE_DEFINE foreach (var lm in lc.Methods) { _ = sb.Append(GenNameArray(lm)); @@ -80,7 +84,7 @@ private string GenType(LoggerClass lc) { _ = sb.Append(GenFormatFunc(lm)); } - +#endif foreach (var lm in lc.Methods) { _ = sb.Append(GenLogMethod(lm)); @@ -112,6 +116,7 @@ partial class {lc.Name} {lc.Constraints} } } +#if !LOGGER_MESSAGE_DEFINE private string GenFormatFunc(LoggerMethod lm) { if (lm.Templates.Count == 0) @@ -248,6 +253,7 @@ private string GenNameArray(LoggerMethod lm) ReturnStringBuilder(sb); } } +#endif private string GenLogMethod(LoggerMethod lm) { @@ -255,6 +261,9 @@ private string GenLogMethod(LoggerMethod lm) if (lm.Level == null) { +#if LOGGER_MESSAGE_DEFINE + level = "global::Microsoft.Extensions.Logging.LogLevel.Debug"; +#else foreach (var p in lm.AllParameters) { if (p.IsLogLevel) @@ -263,6 +272,7 @@ private string GenLogMethod(LoggerMethod lm) break; } } +#endif } else { @@ -321,6 +331,24 @@ private string GenLogMethod(LoggerMethod lm) } } +#if LOGGER_MESSAGE_DEFINE + return $@" + [{_generatedCodeAttribute}] + private static readonly global::System.Action _{lm.Name}Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define{GenDefineTypes(lm, true)}({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message!)}""); + + [{_generatedCodeAttribute}] + {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{GenParameters(lm)}) + {{ + if ({logger}.IsEnabled({level})) + {{ + _{lm.Name}Callback({logger}, {GenCallbackArguments(lm)}{exceptionArg}); + }} + }} + "; + +#else + return $@" [{_generatedCodeAttribute}] {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{GenParameters(lm)}) @@ -336,6 +364,7 @@ private string GenLogMethod(LoggerMethod lm) }} }} "; +#endif } private void AutoGenerateMessage(LoggerMethod lm) @@ -382,6 +411,82 @@ private void AutoGenerateMessage(LoggerMethod lm) } } +#if LOGGER_MESSAGE_DEFINE + private string GenCallbackArguments(LoggerMethod lm) + { + var sb = GetStringBuilder(); + try + { + int count = 0; + foreach (var p in lm.AllParameters) + { + if (p.IsRegular) + { + _ = sb.Append($"{p.Name}, "); + + count++; + if (count == 6) + { + break; + } + } + } + + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenDefineTypes(LoggerMethod lm, bool brackets) + { + var sb = GetStringBuilder(); + try + { + int count = 0; + foreach (var p in lm.AllParameters) + { + if (p.IsRegular) + { + if (count > 0) + { + _ = sb.Append(", "); + } + + _ = sb.Append($"{p.Type}"); + + count++; + if (count == 6) + { + break; + } + } + } + + var result = sb.ToString(); + if (!string.IsNullOrEmpty(result)) + { + if (brackets) + { + result = "<" + result + ">"; + } + else + { + result += ", "; + } + } + + return result; + } + finally + { + ReturnStringBuilder(sb); + } + } +#endif + private string GenParameters(LoggerMethod lm) { var sb = GetStringBuilder(); From bb630326617c7d6d7e678d7dab4ea1b56190da9b Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Fri, 9 Apr 2021 09:56:46 -0700 Subject: [PATCH 076/121] Overhaul of the emitter. - Use LoggerMessage.Define as the implementation mechanism in the common case. This eliminates the need to introduce public LogValues types. - When LoggerMessage.Define is not sufficient, the code generator now emits custom structs to accommodate features that LoggerMessage.Define doesn't support. --- .../gen/LoggerMessageGenerator.Emitter.cs | 593 ++++++++++-------- .../LoggerMessageGeneratedCodeTests.cs | 12 +- ....Extensions.Logging.Generators.Test.csproj | 1 + .../TestClasses/ArgTestExtensions.cs | 4 +- .../TestClasses/CollectionTestExtensions.cs | 9 +- .../TestClasses/MessageTestExtensions.cs | 4 + 6 files changed, 335 insertions(+), 288 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 479662658c2c6..514645963c5be 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -1,7 +1,5 @@ // © Microsoft Corporation. All rights reserved. -//#define LOGGER_MESSAGE_DEFINE - using System.Collections.Generic; using System.Text; using System.Threading; @@ -12,13 +10,8 @@ public partial class LoggerMessageGenerator { internal class Emitter { - // The maximum arity of the LogValues-family of types. Beyond this number, parameters are just kepts in an array (which implies an allocation - // for the array and boxing of all logging method arguments. - private const int MaxLogValuesArity = 6; -#if !LOGGER_MESSAGE_DEFINE - private const int MinLogValuesWithNameArray = 2; -#endif - private const string InternalNamespace = "Microsoft.Extensions.Logging.Internal"; + // The maximum arity of LoggerMessage.Define. + private const int MaxLoggerMessageDefineArguments = 6; private readonly string _generatedCodeAttribute = $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" + @@ -27,7 +20,7 @@ internal class Emitter private readonly Stack _builders = new (); private readonly bool _pascalCaseArguments; private readonly bool _emitDefaultMessage; - + public Emitter(bool pascalCaseArguments, bool emitDefaultMessage = true) { _pascalCaseArguments = pascalCaseArguments; @@ -64,6 +57,13 @@ private static string EscapeMessageString(string message) .Replace("\"", "\\\""); } + private static bool UseLoggerMessageDefine(LoggerMethod lm) + { + return (lm.RegularParameters.Count <= MaxLoggerMessageDefineArguments) + && (lm.Level != null) + && (lm.Templates.Count == lm.RegularParameters.Count); + } + private string GenType(LoggerClass lc) { var sb = GetStringBuilder(); @@ -74,22 +74,18 @@ private string GenType(LoggerClass lc) AutoGenerateMessage(lm); } -#if !LOGGER_MESSAGE_DEFINE foreach (var lm in lc.Methods) { - _ = sb.Append(GenNameArray(lm)); - } + if (!UseLoggerMessageDefine(lm)) + { + _ = sb.Append(GenStruct(lm)); + } - foreach (var lm in lc.Methods) - { - _ = sb.Append(GenFormatFunc(lm)); - } -#endif - foreach (var lm in lc.Methods) - { _ = sb.Append(GenLogMethod(lm)); } + _ = sb.Append(GenEnumerationHelper(lc)); + if (string.IsNullOrWhiteSpace(lc.Namespace)) { return $@" @@ -116,111 +112,127 @@ partial class {lc.Name} {lc.Constraints} } } -#if !LOGGER_MESSAGE_DEFINE - private string GenFormatFunc(LoggerMethod lm) + private string GenStruct(LoggerMethod lm) { - if (lm.Templates.Count == 0) + var constructor = string.Empty; + if (lm.RegularParameters.Count > 0) { - return string.Empty; + constructor = $@" + public __{lm.Name}Struct({GenArguments(lm)}) + {{ +{GenFieldAssignments(lm)} + }} +"; } + var toString = $@" + public override string ToString() + {{ +{GenVariableAssignments(lm)} + return $""{lm.Message}""; + }} +"; + + return $@" + [{_generatedCodeAttribute}] + private readonly struct __{lm.Name}Struct : global::System.Collections.Generic.IReadOnlyList> + {{ +{GenFields(lm)} +{constructor} +{toString} + public static string Format(__{lm.Name}Struct state, global::System.Exception? ex) => state.ToString(); + + public int Count => {lm.RegularParameters.Count + 1}; + + public global::System.Collections.Generic.KeyValuePair this[int index] + {{ + get => index switch + {{ +{GenCases(lm)} + _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case + }}; + }} + + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + {{ +{GenEnumerator(lm)} + }} + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + }} +"; + } + + private string GenFields(LoggerMethod lm) + { var sb = GetStringBuilder(); try { - string typeName; - if (lm.RegularParameters.Count == 1) + foreach (var p in lm.RegularParameters) { - typeName = $"global::{InternalNamespace}.LogValues<{lm.RegularParameters[0].Type}>"; + _ = sb.Append($" private readonly {p.Type} _{p.Name};\n"); } - else if (lm.RegularParameters.Count > MaxLogValuesArity) + + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenFieldAssignments(LoggerMethod lm) + { + var sb = GetStringBuilder(); + try + { + foreach (var p in lm.RegularParameters) { - typeName = $"global::{InternalNamespace}.LogValuesN"; + _ = sb.Append($" this._{p.Name} = {p.Name};\n"); } - else - { - _ = sb.Append($"global::{InternalNamespace}.LogValues<"); + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenVariableAssignments(LoggerMethod lm) + { + var sb = GetStringBuilder(); + try + { + foreach (var t in lm.Templates) + { + int index = 0; foreach (var p in lm.RegularParameters) { - if (p != lm.RegularParameters[0]) + if (p.Name == t) { - _ = sb.Append(", "); + break; } - _ = sb.Append(p.Type); + index++; } - _ = sb.Append('>'); - typeName = sb.ToString(); - _ = sb.Clear(); - } - - foreach (var t in lm.Templates) - { - if (lm.RegularParameters.Count == 1) + // check for an index that's too big, this can happen in some cases of malformed input + if (index < lm.RegularParameters.Count) { - if (lm.RegularParameters[0].IsEnumerable) + if (lm.RegularParameters[index].IsEnumerable) { - _ = sb.Append($" var {t} = global::{InternalNamespace}.ArgumentFormatter.Enumerate(_holder.Value);\n"); + _ = sb.Append($" var {t} = " + + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{t});\n"); } else { - _ = sb.Append($" var {t} = _holder.Value;\n"); - } - } - else - { - int index = 0; - foreach (var p in lm.RegularParameters) - { - if (p.Name == t) - { - break; - } - - index++; - } - - // check for an index that's too big, this can happen in some cases of malformed input - if (index < lm.RegularParameters.Count) - { - if (lm.RegularParameters.Count > MaxLogValuesArity) - { - if (lm.RegularParameters[index].IsEnumerable) - { - _ = sb.Append($" var {t} = " - + $"global::{InternalNamespace}.ArgumentFormatter.Enumerate((global::System.Collections.IEnumerable ?)_holder[{index}].Value);\n"); - } - else - { - _ = sb.Append($" var {t} = _holder[{index}].Value;\n"); - } - } - else - { - if (lm.RegularParameters[index].IsEnumerable) - { - _ = sb.Append($" var {t} = " - + $"global::{InternalNamespace}.ArgumentFormatter.Enumerate((global::System.Collections.IEnumerable ?)_holder.Value{index + 1});\n"); - } - else - { - _ = sb.Append($" var {t} = _holder.Value{index + 1};\n"); - } - } + _ = sb.Append($" var {t} = this._{t};\n"); } } } - return $@" - [{_generatedCodeAttribute}] - private static readonly global::System.Func<{typeName}, global::System.Exception?, string> _format{lm.Name} = (_holder, _) => - {{ -{sb} - global::System.FormattableString fs = $""{EscapeMessageString(lm.Message!)}""; - return global::System.FormattableString.Invariant(fs); - }}; - "; + return sb.ToString(); } finally { @@ -228,24 +240,17 @@ private string GenFormatFunc(LoggerMethod lm) } } - private string GenNameArray(LoggerMethod lm) + private string GenEnumerator(LoggerMethod lm) { - if (lm.RegularParameters.Count is < MinLogValuesWithNameArray or > MaxLogValuesArity) - { - return string.Empty; - } - var sb = GetStringBuilder(); try { - _ = sb.Append($"\n [{_generatedCodeAttribute}]\n"); - _ = sb.Append($" private static readonly string[] _names{lm.Name} = new[] {{ "); foreach (var p in lm.RegularParameters) { - _ = sb.Append($"\"{NormalizeArgumentName(p.Name)}\", "); + _ = sb.Append($" yield return new global::System.Collections.Generic.KeyValuePair(\"{NormalizeArgumentName(p.Name)}\", this._{p.Name});\n"); } - _ = sb.Append("};\n"); + _ = sb.Append($" yield return new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message!)}\");\n"); return sb.ToString(); } finally @@ -253,118 +258,25 @@ private string GenNameArray(LoggerMethod lm) ReturnStringBuilder(sb); } } -#endif - private string GenLogMethod(LoggerMethod lm) + private string GenCases(LoggerMethod lm) { - string level = string.Empty; - - if (lm.Level == null) - { -#if LOGGER_MESSAGE_DEFINE - level = "global::Microsoft.Extensions.Logging.LogLevel.Debug"; -#else - foreach (var p in lm.AllParameters) - { - if (p.IsLogLevel) - { - level = p.Name; - break; - } - } -#endif - } - else - { -#pragma warning disable S109 // Magic numbers should not be used - level = lm.Level switch - { - 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", - 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", - 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", - 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", - 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", - 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", - 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", - _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", - }; -#pragma warning restore S109 // Magic numbers should not be used - } - - string eventName; - if (string.IsNullOrWhiteSpace(lm.EventName)) - { - eventName = $"nameof({lm.Name})"; - } - else - { - eventName = $"\"{lm.EventName}\""; - } - - string exceptionArg = "null"; - foreach (var p in lm.AllParameters) + var sb = GetStringBuilder(); + try { - if (p.IsException) + var index = 0; + foreach (var p in lm.RegularParameters) { - exceptionArg = p.Name; - break; + _ = sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{NormalizeArgumentName(p.Name)}\", this._{p.Name}),\n"); } - } - string formatFunc; - if (lm.Templates.Count != 0) - { - formatFunc = $"_format{lm.Name}"; - } - else - { - formatFunc = $"(_, _) => \"{EscapeMessageString(lm.Message!)}\""; + _ = sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message!)}\"),\n"); + return sb.ToString(); } - - string logger = lm.LoggerField; - foreach (var p in lm.AllParameters) + finally { - if (p.IsLogger) - { - logger = p.Name; - break; - } + ReturnStringBuilder(sb); } - -#if LOGGER_MESSAGE_DEFINE - return $@" - [{_generatedCodeAttribute}] - private static readonly global::System.Action _{lm.Name}Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define{GenDefineTypes(lm, true)}({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message!)}""); - - [{_generatedCodeAttribute}] - {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{GenParameters(lm)}) - {{ - if ({logger}.IsEnabled({level})) - {{ - _{lm.Name}Callback({logger}, {GenCallbackArguments(lm)}{exceptionArg}); - }} - }} - "; - -#else - - return $@" - [{_generatedCodeAttribute}] - {lm.Modifiers} void {lm.Name}({(lm.IsExtensionMethod ? "this " : string.Empty)}{GenParameters(lm)}) - {{ - if ({logger}.IsEnabled({level})) - {{ - {logger}.Log( - {level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - {GenHolder(lm, formatFunc)}, - {exceptionArg}, - {formatFunc}); - }} - }} - "; -#endif } private void AutoGenerateMessage(LoggerMethod lm) @@ -383,7 +295,7 @@ private void AutoGenerateMessage(LoggerMethod lm) if (lm.RegularParameters.Count == 0) { - lm.Message = "{}"; + lm.Message = ""; return; } @@ -411,25 +323,14 @@ private void AutoGenerateMessage(LoggerMethod lm) } } -#if LOGGER_MESSAGE_DEFINE private string GenCallbackArguments(LoggerMethod lm) { var sb = GetStringBuilder(); try { - int count = 0; - foreach (var p in lm.AllParameters) + foreach (var p in lm.RegularParameters) { - if (p.IsRegular) - { - _ = sb.Append($"{p.Name}, "); - - count++; - if (count == 6) - { - break; - } - } + _ = sb.Append($"{p.Name}, "); } return sb.ToString(); @@ -445,24 +346,14 @@ private string GenDefineTypes(LoggerMethod lm, bool brackets) var sb = GetStringBuilder(); try { - int count = 0; - foreach (var p in lm.AllParameters) + foreach (var p in lm.RegularParameters) { - if (p.IsRegular) + if (sb.Length > 0) { - if (count > 0) - { - _ = sb.Append(", "); - } - - _ = sb.Append($"{p.Type}"); - - count++; - if (count == 6) - { - break; - } + _ = sb.Append(", "); } + + _ = sb.Append($"{p.Type}"); } var result = sb.ToString(); @@ -485,7 +376,6 @@ private string GenDefineTypes(LoggerMethod lm, bool brackets) ReturnStringBuilder(sb); } } -#endif private string GenParameters(LoggerMethod lm) { @@ -494,7 +384,7 @@ private string GenParameters(LoggerMethod lm) { foreach (var p in lm.AllParameters) { - if (p != lm.AllParameters[0]) + if (sb.Length > 0) { _ = sb.Append(", "); } @@ -510,69 +400,216 @@ private string GenParameters(LoggerMethod lm) } } - private string GenHolder(LoggerMethod lm, string formatFunc) + private string GenArguments(LoggerMethod lm) { - var originalFormat = EscapeMessageString(lm.Message!); - - if (lm.RegularParameters.Count == 0) + var sb = GetStringBuilder(); + try { - return $"new global::{InternalNamespace}.LogValues({formatFunc}, \"{originalFormat}\")"; - } + foreach (var p in lm.RegularParameters) + { + if (sb.Length > 0) + { + _ = sb.Append(", "); + } + + _ = sb.Append($"{p.Type} {p.Name}"); + } - if (lm.RegularParameters.Count == 1) + return sb.ToString(); + } + finally { - return $"new global::{InternalNamespace}.LogValues<{lm.RegularParameters[0].Type}>" + - $"({formatFunc}, \"{originalFormat}\", \"{NormalizeArgumentName(lm.RegularParameters[0].Name)}\", {lm.RegularParameters[0].Name})"; + ReturnStringBuilder(sb); } + } + + private string GenHolder(LoggerMethod lm) + { + var typeName = $"__{lm.Name}Struct"; var sb = GetStringBuilder(); try { - if (lm.RegularParameters.Count > MaxLogValuesArity) + _ = sb.Append($"new {typeName}("); + foreach (var p in lm.RegularParameters) { - _ = sb.Append($"new global::{InternalNamespace}.LogValuesN({formatFunc}, \"{originalFormat}\", new global::System.Collections.Generic.KeyValuePair[] {{ "); - foreach (var p in lm.RegularParameters) + if (p != lm.RegularParameters[0]) { - _ = sb.Append($"new (\"{NormalizeArgumentName(p.Name)}\", {p.Name}), "); + _ = sb.Append(", "); } - _ = sb.Append("})"); + _ = sb.Append(p.Name); } - else + + _ = sb.Append(')'); + + return sb.ToString(); + } + finally + { + ReturnStringBuilder(sb); + } + } + + private string GenLogMethod(LoggerMethod lm) + { + string level = string.Empty; + + if (lm.Level == null) + { + foreach (var p in lm.AllParameters) { - foreach (var p in lm.RegularParameters) + if (p.IsLogLevel) { - if (p != lm.RegularParameters[0]) - { - _ = sb.Append(", "); - } - - _ = sb.Append(p.Type); + level = p.Name; + break; } + } + } + else + { +#pragma warning disable S109 // Magic numbers should not be used + level = lm.Level switch + { + 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", + 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", + 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", + 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", + 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", + 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", + 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", + _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", + }; +#pragma warning restore S109 // Magic numbers should not be used + } + + string eventName; + if (string.IsNullOrWhiteSpace(lm.EventName)) + { + eventName = $"nameof({lm.Name})"; + } + else + { + eventName = $"\"{lm.EventName}\""; + } + + string exceptionArg = "null"; + foreach (var p in lm.AllParameters) + { + if (p.IsException) + { + exceptionArg = p.Name; + break; + } + } - var tp = sb.ToString(); + string logger = lm.LoggerField; + foreach (var p in lm.AllParameters) + { + if (p.IsLogger) + { + logger = p.Name; + break; + } + } + + var extension = (lm.IsExtensionMethod ? "this " : string.Empty); + + if (UseLoggerMessageDefine(lm)) + { + return $@" + [{_generatedCodeAttribute}] + private static readonly global::System.Action __{lm.Name}Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define{GenDefineTypes(lm, true)}({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message!)}""); - _ = sb.Clear(); - _ = sb.Append($"new global::{InternalNamespace}.LogValues<{tp}>({formatFunc}, \"{originalFormat}\", _names{lm.Name}, "); + [{_generatedCodeAttribute}] + {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm)}) + {{ + if ({logger}.IsEnabled({level})) + {{ + __{lm.Name}Callback({logger}, {GenCallbackArguments(lm)}{exceptionArg}); + }} + }} + "; + } + + return $@" + [{_generatedCodeAttribute}] + {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm)}) + {{ + if ({logger}.IsEnabled({level})) + {{ + {logger}.Log( + {level}, + new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), + {GenHolder(lm)}, + {exceptionArg}, + __{lm.Name}Struct.Format); + }} + }} + "; + } + + private string GenEnumerationHelper(LoggerClass lc) + { + foreach (var lm in lc.Methods) + { + if (UseLoggerMessageDefine(lm)) + { foreach (var p in lm.RegularParameters) { - if (p != lm.RegularParameters[0]) + if (p.IsEnumerable) { - _ = sb.Append(", "); - } + return $@" + [{_generatedCodeAttribute}] + private static string __Enumerate(global::System.Collections.IEnumerable? enumerable) + {{ + if (enumerable == null) + {{ + return ""(null)""; + }} - _ = sb.Append(p.Name); - } + var sb = new global::System.Text.StringBuilder(); + _ = sb.Append('['); - _ = sb.Append(')'); - } + bool first = true; + foreach (object e in enumerable) + {{ + if (!first) + {{ + _ = sb.Append("", ""); + }} + + if (e == null) + {{ + _ = sb.Append(""(null)""); + }} + else + {{ + if (e is global::System.IFormattable fmt) + {{ + _ = sb.Append(fmt.ToString(null, global::System.Globalization.CultureInfo.InvariantCulture)); + }} + else + {{ + _ = sb.Append(e); + }} + }} + + first = false; + }} - return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); + _ = sb.Append(']'); + + return sb.ToString(); + }} +"; + } + } + } } + + return string.Empty; } private string NormalizeArgumentName(string name) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 402d0a2114a25..68045f12528e4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -148,7 +148,11 @@ public void CollectionTest() TestCollection(8, logger); logger.Reset(); - CollectionTestExtensions.M8(logger, LogLevel.Critical, 0, new ArgumentException("Foo"), 1); + CollectionTestExtensions.M8(logger, 0, 1, 2, 3, 4, 5, 6, 7); + TestCollection(9, logger); + + logger.Reset(); + CollectionTestExtensions.M9(logger, LogLevel.Critical, 0, new ArgumentException("Foo"), 1); TestCollection(3, logger); Assert.True(true); @@ -162,14 +166,14 @@ public void MessageTests() logger.Reset(); MessageTestExtensions.M0(logger); Assert.Null(logger.LastException); - Assert.Equal("{}", logger.LastFormattedString); + Assert.Equal(string.Empty, logger.LastFormattedString); Assert.Equal(LogLevel.Trace, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); logger.Reset(); MessageTestExtensions.M1(logger); Assert.Null(logger.LastException); - Assert.Equal("{}", logger.LastFormattedString); + Assert.Equal(string.Empty, logger.LastFormattedString); Assert.Equal(LogLevel.Debug, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); @@ -349,7 +353,7 @@ private static void TestCollection(int expected, MockLogger logger) } Assert.Equal(expected, count); - Assert.Throws(() => _ = rol[expected]); + _ = Assert.Throws(() => _ = rol[expected]); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Test.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Test.csproj index 2a90027b36f93..c1fd29c2684eb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Test.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Test.csproj @@ -16,6 +16,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs index 0f04d525de039..80c152e757a89 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs @@ -29,13 +29,11 @@ internal static partial class ArgTestExtensions [LoggerMessage(6, LogLevel.Error, "M7 {p1}")] public static partial void Method7(ILogger logger, int p1, System.InvalidOperationException p2); - [LoggerMessage(7, LogLevel.Error, "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] #pragma warning disable S107 // Methods should not have too many parameters + [LoggerMessage(7, LogLevel.Error, "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); -#pragma warning restore S107 // Methods should not have too many parameters [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] -#pragma warning disable S107 // Methods should not have too many parameters public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); #pragma warning restore S107 // Methods should not have too many parameters diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs index 263e983ce97d3..44b807225aeee 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs @@ -27,12 +27,15 @@ internal static partial class CollectionTestExtensions [LoggerMessage(6, LogLevel.Error, "M6{p0}{p1}{p2}{p3}{p4}{p5}")] public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); - [LoggerMessage(7, LogLevel.Error, "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] #pragma warning disable S107 // Methods should not have too many parameters + [LoggerMessage(7, LogLevel.Error, "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); + + [LoggerMessage(8, LogLevel.Error, "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] + public static partial void M8(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7); #pragma warning restore S107 // Methods should not have too many parameters - [LoggerMessage(8, "M8{p0}{p1}")] - public static partial void M8(ILogger logger, LogLevel level, int p0, System.Exception ex, int p1); + [LoggerMessage(9, "M8{p0}{p1}")] + public static partial void M9(ILogger logger, LogLevel level, int p0, System.Exception ex, int p1); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index 6395f7e8cc809..e15a98a5f21d9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -1,6 +1,7 @@ // © Microsoft Corporation. All rights reserved. #pragma warning disable CA1801 // Review unused parameters +#pragma warning disable LG0015 namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { @@ -17,5 +18,8 @@ internal static partial class MessageTestExtensions [LoggerMessage(3, LogLevel.Debug, "")] public static partial void M3(ILogger logger, string p1, int p2); + +// [LoggerMessage(4, LogLevel.Debug, "{p1}")] +// public static partial void M4(ILogger logger, string p1, int p2, int p3); } } From 5f210fcd60462732942ac523402e59ef4c398133 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Sat, 10 Apr 2021 09:30:09 -0700 Subject: [PATCH 077/121] Address more feedback and bugs * Remove the code that was warning for the use of DateTime as a parameter. * Fix bug where the code wasn't handling repeating the ssme template multiple times since switching to the new code gen model. * Fix bug where the code wasn't handling out of order templates relative to the logging parameters. This also broke when updating the code gen model. * Remove support for optional message strings, and the JSON encoding that was used in that case. * Make it so specifying a logger template is now a warning instead of an error. * Add three disabled tests (search for TODO) which identify more error cases the parser should report. These checks should be implemented soon. * Remove support for optionally pascal casing arguments. Instead, the exact casing used in the TState's IReadOnlyList is now derived from the template. * Templatet-to-parameter matching is now case-insensitive. * Remove support for global options since there was nothing left to control. --- .../gen/DiagDescriptors.cs | 16 +- .../gen/LoggerMessageGenerator.Emitter.cs | 174 ++++++------------ .../gen/LoggerMessageGenerator.Parser.cs | 108 +++-------- .../gen/LoggerMessageGenerator.cs | 15 +- .../gen/Resources.Designer.cs | 54 ++---- .../gen/Resources.resx | 10 +- .../LoggerMessageGeneratedCodeTests.cs | 60 ++++++ .../LoggerMessageGeneratorEmitterTests.cs | 48 +---- .../LoggerMessageGeneratorParserTests.cs | 80 ++++---- .../TestClasses/MessageTestExtensions.cs | 11 +- .../TestClasses/TemplateTestExtensions.cs | 21 +++ 11 files changed, 245 insertions(+), 352 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs index 34eb33d8bb82d..425334e2101b4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs @@ -102,13 +102,7 @@ internal static class DiagDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor PassingDateTime { get; } = new ( - id: "LG0012", - title: Resources.PassingDateTimeTitle, - messageFormat: Resources.PassingDateTimeMessage, - category: "LoggingGenerator", - DiagnosticSeverity.Warning, - isEnabledByDefault: true); + // TODO: LG0012 is currently unused public static DiagnosticDescriptor ShouldntMentionExceptionInMessage { get; } = new ( id: "LG0013", @@ -150,12 +144,12 @@ internal static class DiagDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor DontMentionLoggerInMessage { get; } = new ( + public static DiagnosticDescriptor ShouldntMentionLoggerInMessage { get; } = new ( id: "LG0018", - title: Resources.DontMentionLoggerInMessageTitle, - messageFormat: Resources.DontMentionLoggerInMessageMessage, + title: Resources.ShouldntMentionLoggerInMessageTitle, + messageFormat: Resources.ShouldntMentionLoggerInMessageMessage, category: "LoggingGenerator", - DiagnosticSeverity.Error, + DiagnosticSeverity.Warning, isEnabledByDefault: true); public static DiagnosticDescriptor MissingLoggerField { get; } = new ( diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 514645963c5be..db94633646908 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -1,5 +1,6 @@ // © Microsoft Corporation. All rights reserved. +using System; using System.Collections.Generic; using System.Text; using System.Threading; @@ -18,14 +19,6 @@ internal class Emitter $"\"{typeof(Emitter).Assembly.GetName().Name}\", " + $"\"{typeof(Emitter).Assembly.GetName().Version}\")"; private readonly Stack _builders = new (); - private readonly bool _pascalCaseArguments; - private readonly bool _emitDefaultMessage; - - public Emitter(bool pascalCaseArguments, bool emitDefaultMessage = true) - { - _pascalCaseArguments = pascalCaseArguments; - _emitDefaultMessage = emitDefaultMessage; - } public string Emit(IReadOnlyList logClasses, CancellationToken cancellationToken) { @@ -59,9 +52,26 @@ private static string EscapeMessageString(string message) private static bool UseLoggerMessageDefine(LoggerMethod lm) { - return (lm.RegularParameters.Count <= MaxLoggerMessageDefineArguments) - && (lm.Level != null) - && (lm.Templates.Count == lm.RegularParameters.Count); + var result = + (lm.RegularParameters.Count <= MaxLoggerMessageDefineArguments) && // more args than LoggerMessage.Define can handle + (lm.Level != null) && // dynamic log level, which LoggerMessage.Define can't handle + (lm.TemplateList.Count == lm.RegularParameters.Count); // mismatch in template to args, which LoggerMessage.Define can't handle + + if (result) + { + // make sure the order of the templates matches the order of the logging method parameter + int count = 0; + foreach (var t in lm.TemplateList) + { + if (!t.Equals(lm.RegularParameters[count].Name, StringComparison.OrdinalIgnoreCase)) + { + // order doesn't match, can't use LoggerMessage.Define + return false; + } + } + } + + return result; } private string GenType(LoggerClass lc) @@ -69,11 +79,6 @@ private string GenType(LoggerClass lc) var sb = GetStringBuilder(); try { - foreach (var lm in lc.Methods) - { - AutoGenerateMessage(lm); - } - foreach (var lm in lc.Methods) { if (!UseLoggerMessageDefine(lm)) @@ -155,7 +160,10 @@ public override string ToString() public global::System.Collections.Generic.IEnumerator> GetEnumerator() {{ -{GenEnumerator(lm)} + for (int i = 0; i < {lm.RegularParameters.Count + 1}; i++) + {{ + yield return this[i]; + }} }} global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); @@ -204,12 +212,12 @@ private string GenVariableAssignments(LoggerMethod lm) var sb = GetStringBuilder(); try { - foreach (var t in lm.Templates) + foreach (var t in lm.TemplateMap) { int index = 0; foreach (var p in lm.RegularParameters) { - if (p.Name == t) + if (t.Key.Equals(p.Name, System.StringComparison.OrdinalIgnoreCase)) { break; } @@ -222,12 +230,12 @@ private string GenVariableAssignments(LoggerMethod lm) { if (lm.RegularParameters[index].IsEnumerable) { - _ = sb.Append($" var {t} = " - + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{t});\n"); + _ = sb.Append($" var {t.Key} = " + + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.RegularParameters[index].Name});\n"); } else { - _ = sb.Append($" var {t} = this._{t};\n"); + _ = sb.Append($" var {t.Key} = this._{lm.RegularParameters[index].Name};\n"); } } } @@ -240,25 +248,6 @@ private string GenVariableAssignments(LoggerMethod lm) } } - private string GenEnumerator(LoggerMethod lm) - { - var sb = GetStringBuilder(); - try - { - foreach (var p in lm.RegularParameters) - { - _ = sb.Append($" yield return new global::System.Collections.Generic.KeyValuePair(\"{NormalizeArgumentName(p.Name)}\", this._{p.Name});\n"); - } - - _ = sb.Append($" yield return new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message!)}\");\n"); - return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } - } - private string GenCases(LoggerMethod lm) { var sb = GetStringBuilder(); @@ -267,55 +256,18 @@ private string GenCases(LoggerMethod lm) var index = 0; foreach (var p in lm.RegularParameters) { - _ = sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{NormalizeArgumentName(p.Name)}\", this._{p.Name}),\n"); - } - - _ = sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message!)}\"),\n"); - return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } - } - - private void AutoGenerateMessage(LoggerMethod lm) - { - if (!string.IsNullOrEmpty(lm.Message)) - { - // already got a message - return; - } - - if (!_emitDefaultMessage) - { - lm.Message = string.Empty; - return; - } - - if (lm.RegularParameters.Count == 0) - { - lm.Message = ""; - return; - } - - var sb = GetStringBuilder(); - try - { - _ = sb.Append("{{"); - foreach (var p in lm.RegularParameters) - { - if (p != lm.RegularParameters[0]) + var name = p.Name; + if (lm.TemplateMap.ContainsKey(name)) { - _ = sb.Append(','); + // take the letter casing from the template + name = lm.TemplateMap[name]; } - _ = sb.Append($"\"{p.Name}\":\"{{{p.Name}}}\""); - _ = lm.Templates.Add(p.Name); + _ = sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),\n"); } - _ = sb.Append("}}"); - lm.Message = sb.ToString(); + _ = sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),\n"); + return sb.ToString(); } finally { @@ -520,7 +472,7 @@ private string GenLogMethod(LoggerMethod lm) return $@" [{_generatedCodeAttribute}] private static readonly global::System.Action __{lm.Name}Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define{GenDefineTypes(lm, true)}({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message!)}""); + global::Microsoft.Extensions.Logging.LoggerMessage.Define{GenDefineTypes(lm, true)}({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message)}""); [{_generatedCodeAttribute}] {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm)}) @@ -532,22 +484,24 @@ private string GenLogMethod(LoggerMethod lm) }} "; } - - return $@" - [{_generatedCodeAttribute}] - {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm)}) - {{ - if ({logger}.IsEnabled({level})) + else + { + return $@" + [{_generatedCodeAttribute}] + {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm)}) {{ - {logger}.Log( - {level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - {GenHolder(lm)}, - {exceptionArg}, - __{lm.Name}Struct.Format); + if ({logger}.IsEnabled({level})) + {{ + {logger}.Log( + {level}, + new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), + {GenHolder(lm)}, + {exceptionArg}, + __{lm.Name}Struct.Format); + }} }} - }} - "; + "; + } } private string GenEnumerationHelper(LoggerClass lc) @@ -612,26 +566,6 @@ private static string __Enumerate(global::System.Collections.IEnumerable? enumer return string.Empty; } - private string NormalizeArgumentName(string name) - { - if (_pascalCaseArguments) - { - var sb = GetStringBuilder(); - try - { - _ = sb.Append(char.ToUpperInvariant(name[0])); - _ = sb.Append(name, 1, name.Length - 1); - name = sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } - } - - return name; - } - // our own cheezy object pool since we can't use the .NET core version (since this code runs in legacy .NET framework) private StringBuilder GetStringBuilder() { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index de6e855a49712..b091908e77358 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -74,13 +74,6 @@ public IReadOnlyList GetLogClasses(IEnumerable(); } - var dateTimeSymbol = _compilation.GetTypeByMetadataName("System.DateTime"); - if (dateTimeSymbol == null) - { - Diag(DiagDescriptors.MissingRequiredType, null, "System.DateTime"); - return Array.Empty(); - } - var results = new List(); var ids = new HashSet(); @@ -130,14 +123,14 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable /// Finds the template arguments contained in the message string. /// - private static void ExtractTemplates(string? message, ISet templates) + private static void ExtractTemplates(string? message, IDictionary templateMap, IList templateList) { if (string.IsNullOrEmpty(message)) { @@ -594,7 +541,9 @@ private static void ExtractTemplates(string? message, ISet templates) // Format item syntax : { index[,alignment][ :formatString] }. var formatDelimiterIndex = FindIndexOfAny(message, _formatDelimiters, openBraceIndex, closeBraceIndex); - _ = templates.Add(message.Substring(openBraceIndex + 1, formatDelimiterIndex - openBraceIndex - 1)); + var templateName = message.Substring(openBraceIndex + 1, formatDelimiterIndex - openBraceIndex - 1); + templateMap[templateName] = templateName; + templateList.Add(templateName); scanIndex = closeBraceIndex + 1; } } @@ -677,9 +626,10 @@ internal class LoggerMethod { public readonly List AllParameters = new (); public readonly List RegularParameters = new (); - public readonly HashSet Templates = new (); + public readonly Dictionary TemplateMap = new (StringComparer.OrdinalIgnoreCase); + public readonly List TemplateList = new (); public string Name = string.Empty; - public string? Message; + public string Message = string.Empty; public int? Level; public int EventId; public string? EventName; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs index bd075ba8b5885..24ec7f9ac6768 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs @@ -32,21 +32,8 @@ public void Execute(GeneratorExecutionContext context) return; } - var pascalCaseArguments = false; - var emitDefaultMessage = true; - - if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("PascalCaseArguments", out var pca)) - { - pascalCaseArguments = (pca.ToUpperInvariant() == "TRUE") || (pca.ToUpperInvariant() == "YES"); - } - - if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("EmitDefaultMessage", out var edm)) - { - emitDefaultMessage = (edm.ToUpperInvariant() == "TRUE") || (edm.ToUpperInvariant() == "YES"); - } - var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); - var e = new Emitter(pascalCaseArguments, emitDefaultMessage); + var e = new Emitter(); var logClasses = p.GetLogClasses(receiver.ClassDeclarations); var result = e.Emit(logClasses, context.CancellationToken); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs index 31956abac02fb..994a379d4c26f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs @@ -78,24 +78,6 @@ internal static string ArgumentHasNoCorrespondingTemplateTitle { } } - /// - /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. - /// - internal static string DontMentionLoggerInMessageMessage { - get { - return ResourceManager.GetString("DontMentionLoggerInMessageMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include logger parameters as templates in the logging message. - /// - internal static string DontMentionLoggerInMessageTitle { - get { - return ResourceManager.GetString("DontMentionLoggerInMessageTitle", resourceCulture); - } - } - /// /// Looks up a localized string similar to Logging method names cannot start with _. /// @@ -330,24 +312,6 @@ internal static string MultipleLoggerFieldsTitle { } } - /// - /// Looks up a localized string similar to No need to supply a DateTime value as argument since the logging infrastructure inserts one implicitly. - /// - internal static string PassingDateTimeMessage { - get { - return ResourceManager.GetString("PassingDateTimeMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No need to supply a DateTime value as argument since the logging infrastructure inserts one implicitly. - /// - internal static string PassingDateTimeTitle { - get { - return ResourceManager.GetString("PassingDateTimeTitle", resourceCulture); - } - } - /// /// Looks up a localized string similar to Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.. /// @@ -384,6 +348,24 @@ internal static string ShouldntMentionExceptionInMessageTitle { } } + /// + /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. + /// + internal static string ShouldntMentionLoggerInMessageMessage { + get { + return ResourceManager.GetString("ShouldntMentionLoggerInMessageMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't include logger parameters as templates in the logging message. + /// + internal static string ShouldntMentionLoggerInMessageTitle { + get { + return ResourceManager.GetString("ShouldntMentionLoggerInMessageTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. /// diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index ad7fac845d403..c9b2277141b25 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -183,12 +183,6 @@ Don't include exception parameters as templates in the logging message - - No need to supply a DateTime value as argument since the logging infrastructure inserts one implicitly - - - No need to supply a DateTime value as argument since the logging infrastructure inserts one implicitly - Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. @@ -225,10 +219,10 @@ Don't include log level parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of - + Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 68045f12528e4..0131ddf0e1b8c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -177,6 +177,7 @@ public void MessageTests() Assert.Equal(LogLevel.Debug, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); +#if false logger.Reset(); MessageTestExtensions.M2(logger, "Foo", "Bar"); Assert.Null(logger.LastException); @@ -190,6 +191,7 @@ public void MessageTests() Assert.Equal("{\"p1\":\"Foo\",\"p2\":\"42\"}", logger.LastFormattedString); Assert.Equal(LogLevel.Debug, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); +#endif } [Fact] @@ -323,6 +325,64 @@ public void EventNameTests() Assert.Equal("CustomEventName", logger.LastEventId.Name); } + [Fact] + public void TemplateTests() + { + var logger = new MockLogger(); + + logger.Reset(); + TemplateTestExtensions.M0(logger, 0); + Assert.Null(logger.LastException); + Assert.Equal("M0 0", logger.LastFormattedString); + AssertLastState(logger, + new KeyValuePair("A1", 0), + new KeyValuePair("{OriginalFormat}", "M0 {A1}")); + + logger.Reset(); + TemplateTestExtensions.M1(logger, 42); + Assert.Null(logger.LastException); + Assert.Equal("M1 42 42", logger.LastFormattedString); + AssertLastState(logger, + new KeyValuePair("A1", 42), + new KeyValuePair("{OriginalFormat}", "M1 {A1} {A1}")); + + logger.Reset(); + TemplateTestExtensions.M2(logger, 42, 43, 44, 45, 46, 47, 48); + Assert.Null(logger.LastException); + Assert.Equal("M2 42 43 44 45 46 47 48", logger.LastFormattedString); + AssertLastState(logger, + new KeyValuePair("A1", 42), + new KeyValuePair("a2", 43), + new KeyValuePair("A3", 44), + new KeyValuePair("a4", 45), + new KeyValuePair("A5", 46), + new KeyValuePair("a6", 47), + new KeyValuePair("A7", 48), + new KeyValuePair("{OriginalFormat}", "M2 {A1} {a2} {A3} {a4} {A5} {a6} {A7}")); + + logger.Reset(); + TemplateTestExtensions.M3(logger, 42, 43); + Assert.Null(logger.LastException); + Assert.Equal("M3 43 42", logger.LastFormattedString); + AssertLastState(logger, + new KeyValuePair("A1", 42), + new KeyValuePair("a2", 43), + new KeyValuePair("{OriginalFormat}", "M3 {a2} {A1}")); + + } + + private static void AssertLastState(MockLogger logger, params KeyValuePair[] expected) + { + var rol = (IReadOnlyList>)logger.LastState!; + int count = 0; + foreach (var kvp in expected) + { + Assert.Equal(kvp.Key, rol[count].Key); + Assert.Equal(kvp.Value, rol[count].Value); + count++; + } + } + private static void TestCollection(int expected, MockLogger logger) { #pragma warning disable SA1009 // Closing parenthesis should be spaced correctly diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index c5e061160647b..2fdad9f7df302 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -1,54 +1,16 @@ // © Microsoft Corporation. All rights reserved. -using System; -using System.Collections.Immutable; using System.IO; using System.Reflection; using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; using Xunit; namespace Microsoft.Extensions.Logging.Generators.Test { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Test")] public class LoggerMessageGeneratorEmitterTests { - private class Options : AnalyzerConfigOptions - { - private readonly string _response; - - public Options(string response) - { - _response = response; - } - - public override bool TryGetValue(string key, out string value) - { - value = _response; - return _response.Length > 0; - } - } - - private class OptionsProvider : AnalyzerConfigOptionsProvider - { - private readonly string _response; - - public OptionsProvider(string response) - { - _response = response; - } - - public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) => throw new NotImplementedException(); - public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) => throw new NotImplementedException(); - public override AnalyzerConfigOptions GlobalOptions => new Options(_response); - } - - [Theory] - [InlineData("")] - [InlineData("TRUE")] - [InlineData("FALSE")] - public async Task TestEmitter(string response) + [Fact] + public async Task TestEmitter() { // This test exists strictly to calculate the code coverage // attained by processing Definitions.cs. The functionality of the @@ -65,19 +27,19 @@ public async Task TestEmitter(string response) @"..\..\..\TestClasses\EnumerableTestExtensions.cs", @"..\..\..\TestClasses\TestInstances.cs", @"..\..\..\TestClasses\CollectionTestExtensions.cs", + @"..\..\..\TestClasses\TemplateTestExtensions.cs", }; foreach (var src in sources) { - var testSourceCode = await File.ReadAllTextAsync(src); + var testSourceCode = await File.ReadAllTextAsync(src).ConfigureAwait(false); var (d, r) = await RoslynTestUtils.RunGenerator( new LoggerMessageGenerator(), #pragma warning disable SA1009 // Closing parenthesis should be spaced correctly new[] { Assembly.GetAssembly(typeof(ILogger))!, Assembly.GetAssembly(typeof(LoggerMessageAttribute))! }, #pragma warning restore SA1009 // Closing parenthesis should be spaced correctly - new[] { testSourceCode }, - optionsProvider: new OptionsProvider(response)).ConfigureAwait(false); + new[] { testSourceCode }).ConfigureAwait(false); Assert.Empty(d); _ = Assert.Single(r); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 7e7c0bbebdcb4..ad52629e9334d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -149,100 +149,102 @@ partial class C "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.DontMentionLoggerInMessage.Id, d[0].Id); + Assert.Equal(DiagDescriptors.ShouldntMentionLoggerInMessage.Id, d[0].Id); } +#if false + // TODO: can't have a log level in both the attribute and as a logging method parameter [Fact] - public async Task InvalidParameterName() + public async Task DoubleLogLevel() { var d = await RunGenerator(@" partial class C { - [LoggerMessage(0, LogLevel.Debug, ""M1 {__foo}"")] - static partial void M1(ILogger logger, string __foo); + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger, LogLevel level); } "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.InvalidLoggingMethodParameterName.Id, d[0].Id); + Assert.Equal(DiagDescriptors.XXX.Id, d[0].Id); } + // TODO: can't have the same template with different casing [Fact] - public async Task DateTimeAsParameterType() + public async Task InconsistentTemplateCasing() { var d = await RunGenerator(@" partial class C { - [LoggerMessage(0, LogLevel.Debug, ""M1 {timeStamp}"")] - static partial void M1(ILogger logger, System.DateTime timeStamp); + [LoggerMessage(0, LogLevel.Debug, ""M1 {p1} {P1}"")] + static partial void M1(ILogger logger, int p1, int P1); } "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.PassingDateTime.Id, d[0].Id); + Assert.Equal(DiagDescriptors.XXX.Id, d[0].Id); } + // TODO: can't have malformed format strings (like dangling {, etc) [Fact] - public async Task NestedType() + public async Task MalformedFormatString() { var d = await RunGenerator(@" partial class C { - public partial class Nested - { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - static partial void M1(ILogger logger); - } + [LoggerMessage(0, LogLevel.Debug, ""M1 {p1} {P1}"")] + static partial void M1(ILogger logger, int p1, int P1); } "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.LoggingMethodInNestedType.Id, d[0].Id); + Assert.Equal(DiagDescriptors.XXX.Id, d[0].Id); } +#endif [Fact] - public async Task MissingExceptionType() + public async Task InvalidParameterName() { var d = await RunGenerator(@" - namespace System - { - public class Object {} - public class Void {} - public class String {} - public struct DateTime {} - } - namespace System.Collections - { - public interface IEnumerable {} - } - namespace Microsoft.Extensions.Logging - { - public enum LogLevel {} - public interface ILogger {} - } - namespace Microsoft.Extensions.Logging + partial class C { - public class LoggerMessageAttribute {} + [LoggerMessage(0, LogLevel.Debug, ""M1 {__foo}"")] + static partial void M1(ILogger logger, string __foo); } + "); + + _ = Assert.Single(d); + Assert.Equal(DiagDescriptors.InvalidLoggingMethodParameterName.Id, d[0].Id); + } + + [Fact] + public async Task NestedType() + { + var d = await RunGenerator(@" partial class C { + public partial class Nested + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void M1(ILogger logger); + } } - ", false, includeBaseReferences: false, includeLoggingReferences: false); + "); _ = Assert.Single(d); - Assert.Equal(DiagDescriptors.MissingRequiredType.Id, d[0].Id); + Assert.Equal(DiagDescriptors.LoggingMethodInNestedType.Id, d[0].Id); } [Fact] - public async Task MissingDateTimeType() + public async Task MissingExceptionType() { var d = await RunGenerator(@" namespace System { public class Object {} public class Void {} - public class Exception {} public class String {} + public struct DateTime {} } namespace System.Collections { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index e15a98a5f21d9..17f3082b7e40b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -13,13 +13,20 @@ internal static partial class MessageTestExtensions [LoggerMessage(1, LogLevel.Debug, "")] public static partial void M1(ILogger logger); +#if false + // These are disabled due to https://github.com/dotnet/roslyn/issues/52527 + // + // These are handled fine by the logger generator and generate warnings. Unfortunately, the above warning suppression is + // not being observed by the C# compiler at the moment, so having these here causes build warnings. + [LoggerMessage(2, LogLevel.Trace)] public static partial void M2(ILogger logger, string p1, string p2); [LoggerMessage(3, LogLevel.Debug, "")] public static partial void M3(ILogger logger, string p1, int p2); -// [LoggerMessage(4, LogLevel.Debug, "{p1}")] -// public static partial void M4(ILogger logger, string p1, int p2, int p3); + [LoggerMessage(4, LogLevel.Debug, "{p1}")] + public static partial void M4(ILogger logger, string p1, int p2, int p3); +#endif } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs new file mode 100644 index 0000000000000..129b638a8ee5c --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs @@ -0,0 +1,21 @@ +// © Microsoft Corporation. All rights reserved. + +#pragma warning disable CA1801 // Review unused parameters + +namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +{ + internal static partial class TemplateTestExtensions + { + [LoggerMessage(0, LogLevel.Error, "M0 {A1}")] + public static partial void M0(ILogger logger, int a1); + + [LoggerMessage(1, LogLevel.Error, "M1 {A1} {A1}")] + public static partial void M1(ILogger logger, int a1); + + [LoggerMessage(2, LogLevel.Error, "M2 {A1} {a2} {A3} {a4} {A5} {a6} {A7}")] + public static partial void M2(ILogger logger, int a1, int a2, int a3, int a4, int a5, int a6, int a7); + + [LoggerMessage(3, LogLevel.Error, "M3 {a2} {A1}")] + public static partial void M3(ILogger logger, int a1, int a2); + } +} From fe54581827aea4c8cc4dc2d719f543b00af74b47 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Sat, 10 Apr 2021 10:26:55 -0700 Subject: [PATCH 078/121] Two nit fixes --- src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx | 2 +- .../LoggerMessageGeneratorParserTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx index ad7fac845d403..bea93e710aeb0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx @@ -178,7 +178,7 @@ Logging methods cannot be generic - Don't include a template for {0} in the logging message since it is implicitly taken care + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include exception parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 7e7c0bbebdcb4..d04aa045ee4fb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -320,7 +320,7 @@ namespace Microsoft.Extensions.Logging { public class LoggerMessageAttribute {} } -R9 partial class C + partial class C { } ", false, includeBaseReferences: false, includeLoggingReferences: false); From b1f5364c937a8db0b42b3742a10f8f38fffee26b Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Sat, 10 Apr 2021 17:12:01 -0400 Subject: [PATCH 079/121] API Review feedback: LoggerMessageAttribute (#4) * API Review feedback: LoggerMessageAttribute * switch event id default to -1 * Update docs regarding message not provided TODO - [ ] disable accepting empty message feature if needed --- .../gen/LoggerMessageGenerator.Parser.cs | 9 +++ .../LoggerMessageGeneratorParserTests.cs | 74 +++++++++---------- .../TestClasses/ArgTestExtensions.cs | 20 ++--- .../TestClasses/CollectionTestExtensions.cs | 20 ++--- .../TestClasses/EnumerableTestExtensions.cs | 20 ++--- .../TestClasses/EventNameTestExtensions.cs | 2 +- .../TestClasses/ExceptionTestExtensions.cs | 4 +- .../TestClasses/LevelTestExtensions.cs | 20 ++--- .../TestClasses/MessageTestExtensions.cs | 10 +-- .../TestClasses/MiscTestExtensions.cs | 6 +- .../TestClasses/SignatureTestExtensions.cs | 24 +++--- .../TestClasses/TemplateTestExtensions.cs | 8 +- .../TestClasses/TestInstances.cs | 4 +- 13 files changed, 113 insertions(+), 108 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index b091908e77358..7bb7df7e9299b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -439,9 +439,18 @@ public IReadOnlyList GetLogClasses(IEnumerable(ILogger logger); } "); @@ -515,25 +515,25 @@ public async Task Templates() var d = await RunGenerator(@" partial class C { - [LoggerMessage(1, LogLevel.Debug, ""M1"")] + [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = ""M1"")] static partial void M1(ILogger logger); - [LoggerMessage(2, LogLevel.Debug, ""M2 {arg1} {arg2}"")] + [LoggerMessage(EventId = 2, Level = LogLevel.Debug, Message = ""M2 {arg1} {arg2}"")] static partial void M2(ILogger logger, string arg1, string arg2); - [LoggerMessage(3, LogLevel.Debug, ""M3 {arg1"")] + [LoggerMessage(EventId = 3, Level = LogLevel.Debug, Message = ""M3 {arg1"")] static partial void M3(ILogger logger); - [LoggerMessage(4, LogLevel.Debug, ""M4 arg1}"")] + [LoggerMessage(EventId = 4, Level = LogLevel.Debug, Message = ""M4 arg1}"")] static partial void M4(ILogger logger); - [LoggerMessage(5, LogLevel.Debug, ""M5 {"")] + [LoggerMessage(EventId = 5, Level = LogLevel.Debug, Message = ""M5 {"")] static partial void M5(ILogger logger); - [LoggerMessage(6, LogLevel.Debug, ""}M6 "")] + [LoggerMessage(EventId = 6, Level = LogLevel.Debug, Message = ""}M6 "")] static partial void M6(ILogger logger); - [LoggerMessage(7, LogLevel.Debug, ""M7 {{arg1}}"")] + [LoggerMessage(EventId = 7, Level = LogLevel.Debug, Message = ""M7 {{arg1}}"")] static partial void M7(ILogger logger); } "); @@ -548,7 +548,7 @@ await Assert.ThrowsAsync(async () => _ = await RunGenerator(@" partial class C { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] static partial void M1(ILogger logger); } ", cancellationToken: new CancellationToken(true))); @@ -561,23 +561,19 @@ public async Task SourceErrors() static partial class C { // bogus argument type - [LoggerMessage(0, "", ""Hello"")] + [LoggerMessage(EventId = 0, Level = "", Message = ""Hello"")] static partial void M1(ILogger logger); // missing parameter name - [LoggerMessage(1, LogLevel.Debug, ""Hello"")] + [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = ""Hello"")] static partial void M2(ILogger); // bogus parameter type - [LoggerMessage(2, LogLevel.Debug, ""Hello"")] + [LoggerMessage(EventId = 2, Level = LogLevel.Debug, Message = ""Hello"")] static partial void M3(XILogger logger); - // bogus enum value - [LoggerMessage(3, LogLevel.Foo, ""Hello"")] - static partial void M4(ILogger logger); - // attribute applied to something other than a method - [LoggerMessage(4, "", ""Hello"")] + [LoggerMessage(EventId = 4, Message = ""Hello"")] int M5; } "); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs index 80c152e757a89..5ca278a9abf29 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs @@ -8,36 +8,36 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { internal static partial class ArgTestExtensions { - [LoggerMessage(0, LogLevel.Error, "M1")] + [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M1")] public static partial void Method1(ILogger logger); - [LoggerMessage(1, LogLevel.Error, "M2 {p1}")] + [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "M2 {p1}")] public static partial void Method2(ILogger logger, string p1); - [LoggerMessage(2, LogLevel.Error, "M3 {p1} {p2}")] + [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "M3 {p1} {p2}")] public static partial void Method3(ILogger logger, string p1, int p2); - [LoggerMessage(3, LogLevel.Error, "M4")] + [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M4")] public static partial void Method4(ILogger logger, InvalidOperationException p1); - [LoggerMessage(4, LogLevel.Error, "M5 {p2}")] + [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M5 {p2}")] public static partial void Method5(ILogger logger, System.InvalidOperationException p1, System.InvalidOperationException p2); - [LoggerMessage(5, LogLevel.Error, "M6 {p2}")] + [LoggerMessage(EventId = 5, Level = LogLevel.Error, Message = "M6 {p2}")] public static partial void Method6(ILogger logger, System.InvalidOperationException p1, int p2); - [LoggerMessage(6, LogLevel.Error, "M7 {p1}")] + [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M7 {p1}")] public static partial void Method7(ILogger logger, int p1, System.InvalidOperationException p2); #pragma warning disable S107 // Methods should not have too many parameters - [LoggerMessage(7, LogLevel.Error, "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] + [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); - [LoggerMessage(8, LogLevel.Error, "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] + [LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); #pragma warning restore S107 // Methods should not have too many parameters - [LoggerMessage(9, LogLevel.Error, "M10{p1}")] + [LoggerMessage(EventId = 9, Level = LogLevel.Error, Message = "M10{p1}")] public static partial void Method10(ILogger logger, int p1); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs index 44b807225aeee..74421589ae4ab 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs @@ -6,36 +6,36 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { internal static partial class CollectionTestExtensions { - [LoggerMessage(0, LogLevel.Error, "M0")] + [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0")] public static partial void M0(ILogger logger); - [LoggerMessage(1, LogLevel.Error, "M1{p0}")] + [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "M1{p0}")] public static partial void M1(ILogger logger, int p0); - [LoggerMessage(2, LogLevel.Error, "M2{p0}{p1}")] + [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "M2{p0}{p1}")] public static partial void M2(ILogger logger, int p0, int p1); - [LoggerMessage(3, LogLevel.Error, "M3{p0}{p1}{p2}")] + [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M3{p0}{p1}{p2}")] public static partial void M3(ILogger logger, int p0, int p1, int p2); - [LoggerMessage(4, LogLevel.Error, "M4{p0}{p1}{p2}{p3}")] + [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M4{p0}{p1}{p2}{p3}")] public static partial void M4(ILogger logger, int p0, int p1, int p2, int p3); - [LoggerMessage(5, LogLevel.Error, "M5{p0}{p1}{p2}{p3}{p4}")] + [LoggerMessage(EventId = 5, Level = LogLevel.Error, Message = "M5{p0}{p1}{p2}{p3}{p4}")] public static partial void M5(ILogger logger, int p0, int p1, int p2, int p3, int p4); - [LoggerMessage(6, LogLevel.Error, "M6{p0}{p1}{p2}{p3}{p4}{p5}")] + [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M6{p0}{p1}{p2}{p3}{p4}{p5}")] public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); #pragma warning disable S107 // Methods should not have too many parameters - [LoggerMessage(7, LogLevel.Error, "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] + [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); - [LoggerMessage(8, LogLevel.Error, "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] + [LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] public static partial void M8(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7); #pragma warning restore S107 // Methods should not have too many parameters - [LoggerMessage(9, "M8{p0}{p1}")] + [LoggerMessage(EventId = 9, Message = "M8{p0}{p1}")] public static partial void M9(ILogger logger, LogLevel level, int p0, System.Exception ex, int p1); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs index 2fbe197c5cf43..25d2766484a7d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs @@ -9,36 +9,36 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { internal static partial class EnumerableTestExtensions { - [LoggerMessage(0, LogLevel.Error, "M0")] + [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0")] public static partial void M0(ILogger logger); - [LoggerMessage(1, LogLevel.Error, "M1{p0}")] + [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "M1{p0}")] public static partial void M1(ILogger logger, IEnumerable p0); - [LoggerMessage(2, LogLevel.Error, "M2{p0}{p1}")] + [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "M2{p0}{p1}")] public static partial void M2(ILogger logger, int p0, IEnumerable p1); - [LoggerMessage(3, LogLevel.Error, "M3{p0}{p1}{p2}")] + [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M3{p0}{p1}{p2}")] public static partial void M3(ILogger logger, int p0, IEnumerable p1, int p2); - [LoggerMessage(4, LogLevel.Error, "M4{p0}{p1}{p2}{p3}")] + [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M4{p0}{p1}{p2}{p3}")] public static partial void M4(ILogger logger, int p0, IEnumerable p1, int p2, int p3); - [LoggerMessage(5, LogLevel.Error, "M5{p0}{p1}{p2}{p3}{p4}")] + [LoggerMessage(EventId = 5, Level = LogLevel.Error, Message = "M5{p0}{p1}{p2}{p3}{p4}")] public static partial void M5(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4); - [LoggerMessage(6, LogLevel.Error, "M6{p0}{p1}{p2}{p3}{p4}{p5}")] + [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M6{p0}{p1}{p2}{p3}{p4}{p5}")] public static partial void M6(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5); #pragma warning disable S107 // Methods should not have too many parameters - [LoggerMessage(7, LogLevel.Error, "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] + [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] public static partial void M7(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6); - [LoggerMessage(8, LogLevel.Error, "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] + [LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] public static partial void M8(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6, int p7); - [LoggerMessage(9, LogLevel.Error, "M9{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}{p8}")] + [LoggerMessage(EventId = 9, Level = LogLevel.Error, Message = "M9{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}{p8}")] public static partial void M9(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs index d3abb21a6892f..7cdfdc82edef7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { internal static partial class EventNameTestExtensions { - [LoggerMessage(0, LogLevel.Trace, "M0", EventName = "CustomEventName")] + [LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "M0", EventName = "CustomEventName")] public static partial void M0(ILogger logger); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs index 5313c2cf7289d..23a780a8f758c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs @@ -8,10 +8,10 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { internal static partial class ExceptionTestExtensions { - [LoggerMessage(0, LogLevel.Trace, "M0 {ex2}")] + [LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "M0 {ex2}")] public static partial void M0(ILogger logger, Exception ex1, Exception ex2); - [LoggerMessage(1, LogLevel.Debug, "M1 {ex2}")] + [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = "M1 {ex2}")] public static partial void M1(Exception ex1, ILogger logger, Exception ex2); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs index 6a339e1f82f89..ac0317765c0a4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs @@ -6,34 +6,34 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { internal static partial class LevelTestExtensions { - [LoggerMessage(0, LogLevel.Trace, "M0")] + [LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "M0")] public static partial void M0(ILogger logger); - [LoggerMessage(1, LogLevel.Debug, "M1")] + [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = "M1")] public static partial void M1(ILogger logger); - [LoggerMessage(2, LogLevel.Information, "M2")] + [LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "M2")] public static partial void M2(ILogger logger); - [LoggerMessage(3, LogLevel.Warning, "M3")] + [LoggerMessage(EventId = 3, Level = LogLevel.Warning, Message = "M3")] public static partial void M3(ILogger logger); - [LoggerMessage(4, LogLevel.Error, "M4")] + [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M4")] public static partial void M4(ILogger logger); - [LoggerMessage(5, LogLevel.Critical, "M5")] + [LoggerMessage(EventId = 5, Level = LogLevel.Critical, Message = "M5")] public static partial void M5(ILogger logger); - [LoggerMessage(6, LogLevel.None, "M6")] + [LoggerMessage(EventId = 6, Level = LogLevel.None, Message = "M6")] public static partial void M6(ILogger logger); - [LoggerMessage(7, (LogLevel)42, "M7")] + [LoggerMessage(EventId = 7, Level = (LogLevel)42, Message = "M7")] public static partial void M7(ILogger logger); - [LoggerMessage(8, "M8")] + [LoggerMessage(EventId = 8, Message = "M8")] public static partial void M8(ILogger logger, LogLevel level); - [LoggerMessage(9, "M9")] + [LoggerMessage(EventId = 9, Message = "M9")] public static partial void M9(LogLevel level, ILogger logger); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index 17f3082b7e40b..9922d0846e9f9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -7,10 +7,10 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { internal static partial class MessageTestExtensions { - [LoggerMessage(0, LogLevel.Trace)] + [LoggerMessage(EventId = 0, Level = LogLevel.Trace)] public static partial void M0(ILogger logger); - [LoggerMessage(1, LogLevel.Debug, "")] + [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = "")] public static partial void M1(ILogger logger); #if false @@ -19,13 +19,13 @@ internal static partial class MessageTestExtensions // These are handled fine by the logger generator and generate warnings. Unfortunately, the above warning suppression is // not being observed by the C# compiler at the moment, so having these here causes build warnings. - [LoggerMessage(2, LogLevel.Trace)] + [LoggerMessage(EventId = 2, Level = LogLevel.Trace)] public static partial void M2(ILogger logger, string p1, string p2); - [LoggerMessage(3, LogLevel.Debug, "")] + [LoggerMessage(EventId = 3, Level = LogLevel.Debug, Message = "")] public static partial void M3(ILogger logger, string p1, int p2); - [LoggerMessage(4, LogLevel.Debug, "{p1}")] + [LoggerMessage(EventId = 4, Level = LogLevel.Debug, Message = "{p1}")] public static partial void M4(ILogger logger, string p1, int p2, int p3); #endif } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs index 9228199c725a9..3ce850d0a554a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs @@ -14,7 +14,7 @@ // Used to test use outside of a namespace internal static partial class NoNamespace { - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")] public static partial void CouldNotOpenSocket(ILogger logger, string hostName); } @@ -23,7 +23,7 @@ namespace Level1 // used to test use inside a one-level namespace internal static partial class OneLevelNamespace { - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")] public static partial void CouldNotOpenSocket(ILogger logger, string hostName); } } @@ -35,7 +35,7 @@ namespace Level2 // used to test use inside a two-level namespace internal static partial class TwoLevelNamespace { - [LoggerMessage(0, LogLevel.Critical, "Could not open socket to `{hostName}`")] + [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")] public static partial void CouldNotOpenSocket(ILogger logger, string hostName); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs index f161f2fd962cf..49036e9ea1e18 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses internal static partial class SignatureTestExtensions { // extension method - [LoggerMessage(eventId: 10, level: LogLevel.Critical, message: "Message11")] + [LoggerMessage(EventId = 10, Level = LogLevel.Critical, Message = "Message11")] internal static partial void M11(this ILogger logger); public static void Combo(ILogger logger) @@ -37,47 +37,47 @@ public static void Combo(ILogger logger, ILogger logger2) } // normal public method - [LoggerMessage(0, LogLevel.Critical, "Message1")] + [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Message1")] public static partial void M1(ILogger logger); // internal method - [LoggerMessage(1, LogLevel.Critical, "Message2")] + [LoggerMessage(EventId = 1, Level = LogLevel.Critical, Message = "Message2")] internal static partial void M2(ILogger logger); // private method - [LoggerMessage(2, LogLevel.Critical, "Message3")] + [LoggerMessage(EventId = 2, Level = LogLevel.Critical, Message = "Message3")] private static partial void M3(ILogger logger); // generic ILogger - [LoggerMessage(3, LogLevel.Critical, "Message4")] + [LoggerMessage(EventId = 3, Level = LogLevel.Critical, Message = "Message4")] private static partial void M4(ILogger logger); // random type method parameter - [LoggerMessage(4, LogLevel.Critical, "Message5 {items}")] + [LoggerMessage(EventId = 4, Level = LogLevel.Critical, Message = "Message5 {items}")] private static partial void M5(ILogger logger, System.Collections.IEnumerable items); // line feeds and quotes in the message string - [LoggerMessage(5, LogLevel.Critical, "Message6\n\"\r")] + [LoggerMessage(EventId = 5, Level = LogLevel.Critical, Message = "Message6\n\"\r")] private static partial void M6(ILogger logger); // generic parameter - [LoggerMessage(6, LogLevel.Critical, "Message7 {p1}\n\"\r")] + [LoggerMessage(EventId = 6, Level = LogLevel.Critical, Message = "Message7 {p1}\n\"\r")] private static partial void M7(ILogger logger, T p1); // normal public method - [LoggerMessage(7, LogLevel.Critical, "Message8")] + [LoggerMessage(EventId = 7, Level = LogLevel.Critical, Message = "Message8")] private protected static partial void M8(ILogger logger); // internal method - [LoggerMessage(8, LogLevel.Critical, "Message9")] + [LoggerMessage(EventId = 8, Level = LogLevel.Critical, Message = "Message9")] protected internal static partial void M9(ILogger logger); // nullable parameter - [LoggerMessage(9, LogLevel.Critical, "Message10 {optional}")] + [LoggerMessage(EventId = 9, Level = LogLevel.Critical, Message = "Message10 {optional}")] internal static partial void M10(ILogger logger, string? optional); // dynamic log level - [LoggerMessage(10, "Message11 {p1} {p2}")] + [LoggerMessage(EventId = 10, Message = "Message11 {p1} {p2}")] internal static partial void M11(ILogger logger, string p1, LogLevel level, string p2); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs index 129b638a8ee5c..f9c873fe9acf7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs @@ -6,16 +6,16 @@ namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses { internal static partial class TemplateTestExtensions { - [LoggerMessage(0, LogLevel.Error, "M0 {A1}")] + [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0 {A1}")] public static partial void M0(ILogger logger, int a1); - [LoggerMessage(1, LogLevel.Error, "M1 {A1} {A1}")] + [LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = "M1 {A1} {A1}")] public static partial void M1(ILogger logger, int a1); - [LoggerMessage(2, LogLevel.Error, "M2 {A1} {a2} {A3} {a4} {A5} {a6} {A7}")] + [LoggerMessage(EventId = 2, Level = LogLevel.Error, Message = "M2 {A1} {a2} {A3} {a4} {A5} {a6} {A7}")] public static partial void M2(ILogger logger, int a1, int a2, int a3, int a4, int a5, int a6, int a7); - [LoggerMessage(3, LogLevel.Error, "M3 {a2} {A1}")] + [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M3 {a2} {A1}")] public static partial void M3(ILogger logger, int a1, int a2); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs index f711722672ea1..ec35448539cb3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs @@ -14,10 +14,10 @@ public TestInstances(ILogger logger) _myLogger = logger; } - [LoggerMessage(0, LogLevel.Error, "M0")] + [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = "M0")] public partial void M0(); - [LoggerMessage(1, LogLevel.Trace, "M1 {p1}")] + [LoggerMessage(EventId = 1, Level = LogLevel.Trace, Message = "M1 {p1}")] public partial void M1(string p1); } } From b7b2928d362428873f026df368fc9623d074e9ee Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Sat, 10 Apr 2021 16:48:21 -0700 Subject: [PATCH 080/121] - Update header license --- .../Microsoft.Extensions.Logging/gen/DiagDescriptors.cs | 3 ++- .../gen/LoggerMessageGenerator.Emitter.cs | 3 ++- .../gen/LoggerMessageGenerator.Parser.cs | 3 ++- .../Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs | 3 ++- .../LoggerMessageGeneratedCodeTests.cs | 3 ++- .../LoggerMessageGeneratorEmitterTests.cs | 3 ++- .../LoggerMessageGeneratorParserTests.cs | 3 ++- .../MockLogger.cs | 3 ++- .../RoslynTestUtils.cs | 3 ++- .../TestClasses/ArgTestExtensions.cs | 3 ++- .../TestClasses/CollectionTestExtensions.cs | 3 ++- .../TestClasses/EnumerableTestExtensions.cs | 3 ++- .../TestClasses/EventNameTestExtensions.cs | 3 ++- .../TestClasses/ExceptionTestExtensions.cs | 3 ++- .../TestClasses/LevelTestExtensions.cs | 3 ++- .../TestClasses/MessageTestExtensions.cs | 3 ++- .../TestClasses/MiscTestExtensions.cs | 3 ++- .../TestClasses/SignatureTestExtensions.cs | 3 ++- .../TestClasses/TemplateTestExtensions.cs | 3 ++- .../TestClasses/TestInstances.cs | 3 ++- 20 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs index 425334e2101b4..c6996bd69dd0e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using Microsoft.CodeAnalysis; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index db94633646908..a0a838499090e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 7bb7df7e9299b..9959bc69b7cdc 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs index 24ec7f9ac6768..d9e583d64eac7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 0131ddf0e1b8c..2bb7ced5a3bae 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 2fdad9f7df302..d99c393537fb5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System.IO; using System.Reflection; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index c9d12d68b60b7..fd1436196145b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs index 6f9bcc368e6a2..b45fbc563c946 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; using Microsoft.Extensions.Logging; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs index 7edad95ebc5ca..e824b135898a5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs index 5ca278a9abf29..0aa6d6ce72b25 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs index 74421589ae4ab..cafa8005c34de 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable CA1801 // Review unused parameters diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs index 25d2766484a7d..7b802cc2ec112 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Collections.Generic; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs index 7cdfdc82edef7..52498e2816f57 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable CA1801 // Review unused parameters diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs index 23a780a8f758c..887817c4c8b86 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using System; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs index ac0317765c0a4..f14aee3313d9e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable CA1801 // Review unused parameters diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index 9922d0846e9f9..4e3f3c15a444d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable CA1801 // Review unused parameters #pragma warning disable LG0015 diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs index 3ce850d0a554a..f7887de0d06f3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.Logging; diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs index 49036e9ea1e18..51785a6995294 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable CA1801 // Review unused parameters #pragma warning disable S1118 diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs index f9c873fe9acf7..135c6215bd5c4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable CA1801 // Review unused parameters diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs index ec35448539cb3..0855b916baf83 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs @@ -1,4 +1,5 @@ -// © Microsoft Corporation. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable CA1801 // Review unused parameters #pragma warning disable CA1822 From c41c46303bd53920bf8d6309a962a375b283e4bc Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Sat, 10 Apr 2021 16:49:46 -0700 Subject: [PATCH 081/121] Minor renames, also added project to solution file --- .../Microsoft.Extensions.Logging.sln | 16 + .../gen/Resources.Designer.cs | 423 ------------------ .../Strings.resx} | 2 +- ...xtensions.Logging.Generators.Tests.csproj} | 2 +- 4 files changed, 18 insertions(+), 425 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs rename src/libraries/Microsoft.Extensions.Logging/gen/{Resources.resx => Resources/Strings.resx} (99%) rename src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/{Microsoft.Extensions.Logging.Generators.Test.csproj => Microsoft.Extensions.Logging.Generators.Tests.csproj} (97%) diff --git a/src/libraries/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.sln b/src/libraries/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.sln index 7d19ffd425c31..d3215a78bbb80 100644 --- a/src/libraries/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.sln +++ b/src/libraries/Microsoft.Extensions.Logging/Microsoft.Extensions.Logging.sln @@ -141,6 +141,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{62569F09-F90 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{35D3ECF9-E321-4AA6-BF5B-41E7AC54A620}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{9E96ED55-37A0-4007-854D-F3E3526E3CC0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators", "gen\Microsoft.Extensions.Logging.Generators.csproj", "{56A5DED2-47C2-4938-931E-B896A6BDDA0D}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.Encodings.Web", "..\System.Text.Encodings.Web\ref\System.Text.Encodings.Web.csproj", "{58614FD7-05BC-46A8-900D-AC5B8622C724}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.Json", "..\System.Text.Json\ref\System.Text.Json.csproj", "{F7E444A4-124D-48E2-B311-17A5ED8B0CC2}" @@ -149,6 +153,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Diagnostics.Diagnost EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Threading.AccessControl", "..\System.Threading.AccessControl\ref\System.Threading.AccessControl.csproj", "{5FF1A443-F491-428F-9121-51523AA65052}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators.Tests", "tests\Microsoft.Extensions.Logging.Generator.Tests\Microsoft.Extensions.Logging.Generators.Tests.csproj", "{8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -419,6 +425,10 @@ Global {1E1B25F0-7B14-4798-BBF4-156A52949CBA}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E1B25F0-7B14-4798-BBF4-156A52949CBA}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E1B25F0-7B14-4798-BBF4-156A52949CBA}.Release|Any CPU.Build.0 = Release|Any CPU + {56A5DED2-47C2-4938-931E-B896A6BDDA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56A5DED2-47C2-4938-931E-B896A6BDDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56A5DED2-47C2-4938-931E-B896A6BDDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56A5DED2-47C2-4938-931E-B896A6BDDA0D}.Release|Any CPU.Build.0 = Release|Any CPU {58614FD7-05BC-46A8-900D-AC5B8622C724}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58614FD7-05BC-46A8-900D-AC5B8622C724}.Debug|Any CPU.Build.0 = Debug|Any CPU {58614FD7-05BC-46A8-900D-AC5B8622C724}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -435,6 +445,10 @@ Global {5FF1A443-F491-428F-9121-51523AA65052}.Debug|Any CPU.Build.0 = Debug|Any CPU {5FF1A443-F491-428F-9121-51523AA65052}.Release|Any CPU.ActiveCfg = Release|Any CPU {5FF1A443-F491-428F-9121-51523AA65052}.Release|Any CPU.Build.0 = Release|Any CPU + {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -506,10 +520,12 @@ Global {504464B5-B163-4D6B-A113-52DDC78A401D} = {35D3ECF9-E321-4AA6-BF5B-41E7AC54A620} {7467E5B1-7454-4277-A84F-E8BE34A80CE4} = {62569F09-F901-4240-B3E1-E2FF90544D74} {1E1B25F0-7B14-4798-BBF4-156A52949CBA} = {35D3ECF9-E321-4AA6-BF5B-41E7AC54A620} + {56A5DED2-47C2-4938-931E-B896A6BDDA0D} = {9E96ED55-37A0-4007-854D-F3E3526E3CC0} {58614FD7-05BC-46A8-900D-AC5B8622C724} = {62569F09-F901-4240-B3E1-E2FF90544D74} {F7E444A4-124D-48E2-B311-17A5ED8B0CC2} = {62569F09-F901-4240-B3E1-E2FF90544D74} {5B5AFA97-C1FC-47B9-AB7A-6A10E0285282} = {62569F09-F901-4240-B3E1-E2FF90544D74} {5FF1A443-F491-428F-9121-51523AA65052} = {62569F09-F901-4240-B3E1-E2FF90544D74} + {8C0151F9-1FC9-4D9B-A5B1-60C7B8E7B761} = {88957302-AFDD-4923-BF5A-336EAB5F28B7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B57B7C13-740F-4482-B7B6-B5E87014ACB1} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs deleted file mode 100644 index 994a379d4c26f..0000000000000 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.Designer.cs +++ /dev/null @@ -1,423 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Microsoft.Extensions.Logging.Generators { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Extensions.Logging.Generators.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Argument {0} is not referenced from the logging message. - /// - internal static string ArgumentHasNoCorrespondingTemplateMessage { - get { - return ResourceManager.GetString("ArgumentHasNoCorrespondingTemplateMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Argument is not referenced from the logging message. - /// - internal static string ArgumentHasNoCorrespondingTemplateTitle { - get { - return ResourceManager.GetString("ArgumentHasNoCorrespondingTemplateTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging method names cannot start with _. - /// - internal static string InvalidLoggingMethodNameMessage { - get { - return ResourceManager.GetString("InvalidLoggingMethodNameMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging method names cannot start with _. - /// - internal static string InvalidLoggingMethodNameTitle { - get { - return ResourceManager.GetString("InvalidLoggingMethodNameTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging method parameter names cannot start with _. - /// - internal static string InvalidLoggingMethodParameterNameMessage { - get { - return ResourceManager.GetString("InvalidLoggingMethodParameterNameMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging method parameter names cannot start with _. - /// - internal static string InvalidLoggingMethodParameterNameTitle { - get { - return ResourceManager.GetString("InvalidLoggingMethodParameterNameTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods cannot have a body. - /// - internal static string LoggingMethodHasBodyMessage { - get { - return ResourceManager.GetString("LoggingMethodHasBodyMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods cannot have a body. - /// - internal static string LoggingMethodHasBodyTitle { - get { - return ResourceManager.GetString("LoggingMethodHasBodyTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging class cannot be in nested types. - /// - internal static string LoggingMethodInNestedTypeMessage { - get { - return ResourceManager.GetString("LoggingMethodInNestedTypeMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging class cannot be in nested types. - /// - internal static string LoggingMethodInNestedTypeTitle { - get { - return ResourceManager.GetString("LoggingMethodInNestedTypeTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods cannot be generic. - /// - internal static string LoggingMethodIsGenericMessage { - get { - return ResourceManager.GetString("LoggingMethodIsGenericMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods cannot be generic. - /// - internal static string LoggingMethodIsGenericTitle { - get { - return ResourceManager.GetString("LoggingMethodIsGenericTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods must be partial. - /// - internal static string LoggingMethodMustBePartialMessage { - get { - return ResourceManager.GetString("LoggingMethodMustBePartialMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods must be partial. - /// - internal static string LoggingMethodMustBePartialTitle { - get { - return ResourceManager.GetString("LoggingMethodMustBePartialTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods must return void. - /// - internal static string LoggingMethodMustReturnVoidMessage { - get { - return ResourceManager.GetString("LoggingMethodMustReturnVoidMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods must return void. - /// - internal static string LoggingMethodMustReturnVoidTitle { - get { - return ResourceManager.GetString("LoggingMethodMustReturnVoidTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods must be static. - /// - internal static string LoggingMethodShouldBeStaticMessage { - get { - return ResourceManager.GetString("LoggingMethodShouldBeStaticMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging methods must be static. - /// - internal static string LoggingMethodShouldBeStaticTitle { - get { - return ResourceManager.GetString("LoggingMethodShouldBeStaticTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. - /// - internal static string MissingLoggerArgumentMessage { - get { - return ResourceManager.GetString("MissingLoggerArgumentMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface. - /// - internal static string MissingLoggerArgumentTitle { - get { - return ResourceManager.GetString("MissingLoggerArgumentTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0}. - /// - internal static string MissingLoggerFieldMessage { - get { - return ResourceManager.GetString("MissingLoggerFieldMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Couldn't find a field of type Microsoft.Extensions.Logging.ILogger. - /// - internal static string MissingLoggerFieldTitle { - get { - return ResourceManager.GetString("MissingLoggerFieldTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. - /// - internal static string MissingLogLevelMessage { - get { - return ResourceManager.GetString("MissingLogLevelMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method. - /// - internal static string MissingLogLevelTitle { - get { - return ResourceManager.GetString("MissingLogLevelTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not find definition for type {0}. - /// - internal static string MissingRequiredTypeMessage { - get { - return ResourceManager.GetString("MissingRequiredTypeMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not find a required type definition. - /// - internal static string MissingRequiredTypeTitle { - get { - return ResourceManager.GetString("MissingRequiredTypeTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0}. - /// - internal static string MultipleLoggerFieldsMessage { - get { - return ResourceManager.GetString("MultipleLoggerFieldsMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Found multiple fields of type Microsoft.Extensions.Logging.ILogger. - /// - internal static string MultipleLoggerFieldsTitle { - get { - return ResourceManager.GetString("MultipleLoggerFieldsTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.. - /// - internal static string RedundantQualifierInMessageMessage { - get { - return ResourceManager.GetString("RedundantQualifierInMessageMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Redundant qualifier in logging message. - /// - internal static string RedundantQualifierInMessageTitle { - get { - return ResourceManager.GetString("RedundantQualifierInMessageTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care. - /// - internal static string ShouldntMentionExceptionInMessageMessage { - get { - return ResourceManager.GetString("ShouldntMentionExceptionInMessageMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include exception parameters as templates in the logging message. - /// - internal static string ShouldntMentionExceptionInMessageTitle { - get { - return ResourceManager.GetString("ShouldntMentionExceptionInMessageTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. - /// - internal static string ShouldntMentionLoggerInMessageMessage { - get { - return ResourceManager.GetString("ShouldntMentionLoggerInMessageMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include logger parameters as templates in the logging message. - /// - internal static string ShouldntMentionLoggerInMessageTitle { - get { - return ResourceManager.GetString("ShouldntMentionLoggerInMessageTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include a template for {0} in the logging message since it is implicitly taken care of. - /// - internal static string ShouldntMentionLogLevelInMessageMessage { - get { - return ResourceManager.GetString("ShouldntMentionLogLevelInMessageMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't include log level parameters as templates in the logging message. - /// - internal static string ShouldntMentionLogLevelInMessageTitle { - get { - return ResourceManager.GetString("ShouldntMentionLogLevelInMessageTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Multiple logging methods are using event id {0} in class {1}. - /// - internal static string ShouldntReuseEventIdsMessage { - get { - return ResourceManager.GetString("ShouldntReuseEventIdsMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Multiple logging methods cannot use the same event id within a class. - /// - internal static string ShouldntReuseEventIdsTitle { - get { - return ResourceManager.GetString("ShouldntReuseEventIdsTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Template {0} is not provided as argument to the logging method. - /// - internal static string TemplateHasNoCorrespondingArgumentMessage { - get { - return ResourceManager.GetString("TemplateHasNoCorrespondingArgumentMessage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Logging template has no corresponding method argument. - /// - internal static string TemplateHasNoCorrespondingArgumentTitle { - get { - return ResourceManager.GetString("TemplateHasNoCorrespondingArgumentTitle", resourceCulture); - } - } - } -} diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx similarity index 99% rename from src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx rename to src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx index 2e48404ed845d..d5d5aa0803368 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx @@ -1,4 +1,4 @@ - + 6.0.0-beta.21207.4 6.0.0-beta.21207.4 @@ -154,6 +156,7 @@ 2.4.2 1.3.0 12.0.3 + 2.0.4 4.12.0 2.14.3 diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 1693da9b7a4e1..4ada1f2dfe543 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -17,8 +17,9 @@ - - + + + From 946f764fff0c45767c26716141edf359245f30ce Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Mon, 12 Apr 2021 19:29:53 -0700 Subject: [PATCH 088/121] - Switch ActiveIssue from closed to dupe one. - Add attribute at the type level. --- .../LoggerMessageGeneratedCodeTests.cs | 11 +------ .../LoggerMessageGeneratorEmitterTests.cs | 2 +- .../LoggerMessageGeneratorParserTests.cs | 31 +------------------ 3 files changed, 3 insertions(+), 41 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index f431719a68f00..bd3e403bd8f94 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -8,10 +8,10 @@ namespace Microsoft.Extensions.Logging.Generators.Test { + [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)] public class LoggerMessageGeneratedCodeTests { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void BasicTests() { var logger = new MockLogger(); @@ -39,7 +39,6 @@ public void BasicTests() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void EnableTest() { var logger = new MockLogger(); @@ -51,7 +50,6 @@ public void EnableTest() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void ArgTest() { var logger = new MockLogger(); @@ -115,7 +113,6 @@ public void ArgTest() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void CollectionTest() { var logger = new MockLogger(); @@ -164,7 +161,6 @@ public void CollectionTest() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void MessageTests() { var logger = new MockLogger(); @@ -201,7 +197,6 @@ public void MessageTests() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void InstanceTests() { var logger = new MockLogger(); @@ -223,7 +218,6 @@ public void InstanceTests() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void LevelTests() { var logger = new MockLogger(); @@ -300,7 +294,6 @@ public void LevelTests() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void ExceptionTests() { var logger = new MockLogger(); @@ -321,7 +314,6 @@ public void ExceptionTests() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void EventNameTests() { var logger = new MockLogger(); @@ -336,7 +328,6 @@ public void EventNameTests() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public void TemplateTests() { var logger = new MockLogger(); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 3135fcb93acd5..ea95a238c6452 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -8,10 +8,10 @@ namespace Microsoft.Extensions.Logging.Generators.Test { + [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)] public class LoggerMessageGeneratorEmitterTests { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task TestEmitter() { // The functionality of the resulting code is tested via LoggerMessageGeneratedCodeTests.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 8b3b3927c3c67..195ec7251d9bf 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -11,11 +11,11 @@ namespace Microsoft.Extensions.Logging.Generators.Test { + [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Test")] public class LoggerMessageGeneratorParserTests { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task InvalidMethodName() { var d = await RunGenerator(@" @@ -31,7 +31,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingLogLevel() { var d = await RunGenerator(@" @@ -47,7 +46,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task InvalidMethodBody() { var d = await RunGenerator(@" @@ -67,7 +65,6 @@ static partial void M1(ILogger logger) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingTemplate() { var d = await RunGenerator(@" @@ -83,7 +80,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingArgument() { var d = await RunGenerator(@" @@ -99,7 +95,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task NeedlessQualifierInMessage() { var d = await RunGenerator(@" @@ -115,7 +110,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task NeedlessExceptionInMessage() { var d = await RunGenerator(@" @@ -146,7 +140,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task NeedlessLoggerInMessage() { var d = await RunGenerator(@" @@ -164,7 +157,6 @@ partial class C #if false // TODO: can't have a log level in both the attribute and as a logging method parameter [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task DoubleLogLevel() { var d = await RunGenerator(@" @@ -181,7 +173,6 @@ partial class C // TODO: can't have the same template with different casing [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task InconsistentTemplateCasing() { var d = await RunGenerator(@" @@ -198,7 +189,6 @@ partial class C // TODO: can't have malformed format strings (like dangling {, etc) [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MalformedFormatString() { var d = await RunGenerator(@" @@ -215,7 +205,6 @@ partial class C #endif [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task InvalidParameterName() { var d = await RunGenerator(@" @@ -231,7 +220,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task NestedType() { var d = await RunGenerator(@" @@ -250,7 +238,6 @@ public partial class Nested } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingExceptionType() { var d = await RunGenerator(@" @@ -284,7 +271,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingStringType() { var d = await RunGenerator(@" @@ -318,7 +304,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingEnumerableType() { var d = await RunGenerator(@" @@ -349,7 +334,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingLoggerMessageAttributeType() { var d = await RunGenerator(@" @@ -362,7 +346,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingILoggerType() { var d = await RunGenerator(@" @@ -379,7 +362,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingLogLevelType() { var d = await RunGenerator(@" @@ -400,7 +382,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task EventIdReuse() { var d = await RunGenerator(@" @@ -420,7 +401,6 @@ partial class MyClass } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MethodReturnType() { var d = await RunGenerator(@" @@ -438,7 +418,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MissingILogger() { var d = await RunGenerator(@" @@ -454,7 +433,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task NotStatic() { var d = await RunGenerator(@" @@ -470,7 +448,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task NoILoggerField() { var d = await RunGenerator(@" @@ -486,7 +463,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MultipleILoggerFields() { var d = await RunGenerator(@" @@ -505,7 +481,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task NotPartial() { var d = await RunGenerator(@" @@ -522,7 +497,6 @@ static void M1(ILogger logger) {} } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task MethodGeneric() { var d = await RunGenerator(@" @@ -538,7 +512,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task Templates() { var d = await RunGenerator(@" @@ -571,7 +544,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task Cancellation() { await Assert.ThrowsAsync(async () => @@ -585,7 +557,6 @@ partial class C } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34091", TestRuntimes.Mono)] public async Task SourceErrors() { var d = await RunGenerator(@" From 80840e667fff4d5dc847af77aaaf11874a9aff1d Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Mon, 12 Apr 2021 19:36:56 -0700 Subject: [PATCH 089/121] - Move RoslynTestUtils to common folder - Correct namespaces --- .../tests/SourceGenerators}/RoslynTestUtils.cs | 2 +- .../LoggerMessageGeneratedCodeTests.cs | 4 ++-- .../LoggerMessageGeneratorEmitterTests.cs | 3 ++- .../LoggerMessageGeneratorParserTests.cs | 3 ++- .../Microsoft.Extensions.Logging.Generators.Tests.csproj | 5 +++++ .../MockLogger.cs | 2 +- .../TestClasses/ArgTestExtensions.cs | 2 +- .../TestClasses/CollectionTestExtensions.cs | 2 +- .../TestClasses/EnumerableTestExtensions.cs | 2 +- .../TestClasses/EventNameTestExtensions.cs | 2 +- .../TestClasses/ExceptionTestExtensions.cs | 2 +- .../TestClasses/LevelTestExtensions.cs | 2 +- .../TestClasses/MessageTestExtensions.cs | 2 +- .../TestClasses/SignatureTestExtensions.cs | 2 +- .../TestClasses/TemplateTestExtensions.cs | 2 +- .../TestClasses/TestInstances.cs | 2 +- 16 files changed, 23 insertions(+), 16 deletions(-) rename src/libraries/{Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests => Common/tests/SourceGenerators}/RoslynTestUtils.cs (99%) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs similarity index 99% rename from src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs rename to src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs index e824b135898a5..139cdf40e7e44 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/RoslynTestUtils.cs +++ b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs @@ -17,7 +17,7 @@ using Microsoft.CodeAnalysis.Text; using Xunit; -namespace Microsoft.Extensions.Logging.Generators.Test +namespace SourceGenerators.Tests { internal static class RoslynTestUtils { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index bd3e403bd8f94..fb7b7d2d710a1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; -using Microsoft.Extensions.Logging.Generators.Test.TestClasses; +using Microsoft.Extensions.Logging.Generators.Tests.TestClasses; using Xunit; -namespace Microsoft.Extensions.Logging.Generators.Test +namespace Microsoft.Extensions.Logging.Generators.Tests { [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)] public class LoggerMessageGeneratedCodeTests diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index ea95a238c6452..df25d664a639a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -4,9 +4,10 @@ using System.IO; using System.Reflection; using System.Threading.Tasks; +using SourceGenerators.Tests; using Xunit; -namespace Microsoft.Extensions.Logging.Generators.Test +namespace Microsoft.Extensions.Logging.Generators.Tests { [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)] public class LoggerMessageGeneratorEmitterTests diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 195ec7251d9bf..10052ee039af0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -7,9 +7,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using SourceGenerators.Tests; using Xunit; -namespace Microsoft.Extensions.Logging.Generators.Test +namespace Microsoft.Extensions.Logging.Generators.Tests { [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Test")] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 664b231df12ef..a9468360e795a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -7,6 +7,11 @@ true + + + + diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs index b45fbc563c946..97fef16001244 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -4,7 +4,7 @@ using System; using Microsoft.Extensions.Logging; -namespace Microsoft.Extensions.Logging.Generators.Test +namespace Microsoft.Extensions.Logging.Generators.Tests { /// /// A logger which captures the last log state logged to it. diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs index 0aa6d6ce72b25..83739f53dc618 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs @@ -5,7 +5,7 @@ #pragma warning disable CA1801 // Review unused parameters -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class ArgTestExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs index cafa8005c34de..113762d925e45 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs @@ -3,7 +3,7 @@ #pragma warning disable CA1801 // Review unused parameters -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class CollectionTestExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs index 7b802cc2ec112..c90928aa3d87d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs @@ -6,7 +6,7 @@ #pragma warning disable CA1801 // Review unused parameters -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class EnumerableTestExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs index 52498e2816f57..1d0529c648c51 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs @@ -3,7 +3,7 @@ #pragma warning disable CA1801 // Review unused parameters -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class EventNameTestExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs index 887817c4c8b86..c405b3d4000d2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs @@ -5,7 +5,7 @@ #pragma warning disable CA1801 // Review unused parameters -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class ExceptionTestExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs index f14aee3313d9e..0b78b8e0c25d1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs @@ -3,7 +3,7 @@ #pragma warning disable CA1801 // Review unused parameters -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class LevelTestExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index 4e3f3c15a444d..f09341df0673b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -4,7 +4,7 @@ #pragma warning disable CA1801 // Review unused parameters #pragma warning disable LG0015 -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class MessageTestExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs index 51785a6995294..960a220f6add1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs @@ -4,7 +4,7 @@ #pragma warning disable CA1801 // Review unused parameters #pragma warning disable S1118 -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { // test particular method signature variations are generated correctly internal static partial class SignatureTestExtensions diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs index 135c6215bd5c4..33a5ef073dc6e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs @@ -3,7 +3,7 @@ #pragma warning disable CA1801 // Review unused parameters -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class TemplateTestExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs index 0855b916baf83..67bde9243dd20 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs @@ -4,7 +4,7 @@ #pragma warning disable CA1801 // Review unused parameters #pragma warning disable CA1822 -namespace Microsoft.Extensions.Logging.Generators.Test.TestClasses +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { public partial class TestInstances { From 5637b69d26b62135314a93fc04ecda2668608d53 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 06:02:31 -0700 Subject: [PATCH 090/121] Remove suppressed warnings --- .../gen/LoggerMessageGenerator.Emitter.cs | 2 -- .../gen/LoggerMessageGenerator.Parser.cs | 10 ---------- .../src/LoggerMessageAttribute.cs | 2 -- .../LoggerMessageGeneratedCodeTests.cs | 2 -- .../LoggerMessageGeneratorEmitterTests.cs | 2 -- .../LoggerMessageGeneratorParserTests.cs | 5 ----- .../TestClasses/ArgTestExtensions.cs | 4 ---- .../TestClasses/CollectionTestExtensions.cs | 4 ---- .../TestClasses/EnumerableTestExtensions.cs | 4 ---- .../TestClasses/EventNameTestExtensions.cs | 2 -- .../TestClasses/ExceptionTestExtensions.cs | 2 -- .../TestClasses/LevelTestExtensions.cs | 2 -- .../TestClasses/MessageTestExtensions.cs | 3 --- .../TestClasses/MiscTestExtensions.cs | 9 --------- .../TestClasses/SignatureTestExtensions.cs | 3 --- .../TestClasses/TemplateTestExtensions.cs | 2 -- .../TestClasses/TestInstances.cs | 3 --- 17 files changed, 61 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index a0a838499090e..b9d3d1f9922d3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -421,7 +421,6 @@ private string GenLogMethod(LoggerMethod lm) } else { -#pragma warning disable S109 // Magic numbers should not be used level = lm.Level switch { 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", @@ -433,7 +432,6 @@ private string GenLogMethod(LoggerMethod lm) 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", }; -#pragma warning restore S109 // Magic numbers should not be used } string eventName; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 9959bc69b7cdc..1790cf142a33e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -194,14 +194,12 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable 0 && message[scanIndex] != brace) { -#pragma warning disable S109 // Magic numbers should not be used if (braceOccurrenceCount % 2 == 0) -#pragma warning restore S109 // Magic numbers should not be used { // Even number of '{' or '}' found. Proceed search with next occurrence of '{' or '}'. braceOccurrenceCount = 0; @@ -616,8 +608,6 @@ private static int FindIndexOfAny(string message, char[] chars, int startIndex, } } -#pragma warning disable SA1401 // Fields should be private - /// /// A logger class holding a bunch of logger methods. /// diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs index 4d6900b92c607..081e4bfe6ea84 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs @@ -11,7 +11,6 @@ namespace Microsoft.Extensions.Logging [AttributeUsage(AttributeTargets.Method)] public sealed class LoggerMessageAttribute : Attribute { -#pragma warning disable SA1629 // Documentation text should end with a period /// /// Initializes a new instance of the class /// which is used to guide the production of a strongly-typed logging method. @@ -33,7 +32,6 @@ public sealed class LoggerMessageAttribute : Attribute /// } /// public LoggerMessageAttribute() { } -#pragma warning restore SA1629 // Documentation text should end with a period /// /// Gets the logging event id for the logging method. diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index fb7b7d2d710a1..aaaf9d6f1a0c1 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -387,9 +387,7 @@ private static void AssertLastState(MockLogger logger, params KeyValuePair>)!; -#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly Assert.NotNull(rol); Assert.Equal(expected, rol.Count); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index df25d664a639a..f155b03391520 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -23,9 +23,7 @@ public async Task TestEmitter() var (d, r) = await RoslynTestUtils.RunGenerator( new LoggerMessageGenerator(), -#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly new[] { Assembly.GetAssembly(typeof(ILogger))!, Assembly.GetAssembly(typeof(LoggerMessageAttribute))! }, -#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly new[] { testSourceCode }).ConfigureAwait(false); Assert.Empty(d); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 10052ee039af0..2bd864ae0c726 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -13,7 +13,6 @@ namespace Microsoft.Extensions.Logging.Generators.Tests { [ActiveIssue("https://github.com/dotnet/runtime/issues/32743", TestRuntimes.Mono)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Test")] public class LoggerMessageGeneratorParserTests { [Fact] @@ -611,14 +610,10 @@ private static async Task> RunGenerator( "; } -#pragma warning disable SA1011 // Closing square brackets should be spaced correctly Assembly[]? refs = null; -#pragma warning restore SA1011 // Closing square brackets should be spaced correctly if (includeLoggingReferences) { -#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly refs = new[] { Assembly.GetAssembly(typeof(ILogger))!, Assembly.GetAssembly(typeof(LoggerMessageAttribute))! }; -#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly } var (d, r) = await RoslynTestUtils.RunGenerator( diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs index 83739f53dc618..b0f1cb39eaef2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ArgTestExtensions.cs @@ -3,8 +3,6 @@ using System; -#pragma warning disable CA1801 // Review unused parameters - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class ArgTestExtensions @@ -30,13 +28,11 @@ internal static partial class ArgTestExtensions [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M7 {p1}")] public static partial void Method7(ILogger logger, int p1, System.InvalidOperationException p2); -#pragma warning disable S107 // Methods should not have too many parameters [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M8{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] public static partial void Method8(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); [LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}")] public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); -#pragma warning restore S107 // Methods should not have too many parameters [LoggerMessage(EventId = 9, Level = LogLevel.Error, Message = "M10{p1}")] public static partial void Method10(ILogger logger, int p1); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs index 113762d925e45..74d4b996ee709 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/CollectionTestExtensions.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#pragma warning disable CA1801 // Review unused parameters - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class CollectionTestExtensions @@ -28,13 +26,11 @@ internal static partial class CollectionTestExtensions [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M6{p0}{p1}{p2}{p3}{p4}{p5}")] public static partial void M6(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5); -#pragma warning disable S107 // Methods should not have too many parameters [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] public static partial void M7(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6); [LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = "M8{p0}{p1}{p2}{p3}{p4}{p5}{p6}{p7}")] public static partial void M8(ILogger logger, int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7); -#pragma warning restore S107 // Methods should not have too many parameters [LoggerMessage(EventId = 9, Message = "M8{p0}{p1}")] public static partial void M9(ILogger logger, LogLevel level, int p0, System.Exception ex, int p1); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs index c90928aa3d87d..65cfd41a1347c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EnumerableTestExtensions.cs @@ -4,8 +4,6 @@ using System.Collections; using System.Collections.Generic; -#pragma warning disable CA1801 // Review unused parameters - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class EnumerableTestExtensions @@ -31,8 +29,6 @@ internal static partial class EnumerableTestExtensions [LoggerMessage(EventId = 6, Level = LogLevel.Error, Message = "M6{p0}{p1}{p2}{p3}{p4}{p5}")] public static partial void M6(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5); -#pragma warning disable S107 // Methods should not have too many parameters - [LoggerMessage(EventId = 7, Level = LogLevel.Error, Message = "M7{p0}{p1}{p2}{p3}{p4}{p5}{p6}")] public static partial void M7(ILogger logger, int p0, IEnumerable p1, int p2, int p3, int p4, int p5, int p6); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs index 1d0529c648c51..4c0ddf320aabf 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/EventNameTestExtensions.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#pragma warning disable CA1801 // Review unused parameters - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class EventNameTestExtensions diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs index c405b3d4000d2..45b01e56b2978 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/ExceptionTestExtensions.cs @@ -3,8 +3,6 @@ using System; -#pragma warning disable CA1801 // Review unused parameters - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class ExceptionTestExtensions diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs index 0b78b8e0c25d1..5726aa02a4c3f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/LevelTestExtensions.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#pragma warning disable CA1801 // Review unused parameters - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class LevelTestExtensions diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index f09341df0673b..91ff8b36fbe66 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#pragma warning disable CA1801 // Review unused parameters -#pragma warning disable LG0015 - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class MessageTestExtensions diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs index f7887de0d06f3..5abecef389906 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs @@ -3,15 +3,6 @@ using Microsoft.Extensions.Logging; -#pragma warning disable CA1801 // Review unused parameters -#pragma warning disable S1118 // Utility classes should not have public constructors -#pragma warning disable S3903 // Types should be defined in named namespaces -#pragma warning disable SA1202 // Elements should be ordered by access -#pragma warning disable SA1204 // Static elements should appear before instance elements -#pragma warning disable SA1207 // Protected should come before internal -#pragma warning disable SA1402 // File may only contain a single type -#pragma warning disable SA1403 // File may only contain a single namespace - // Used to test use outside of a namespace internal static partial class NoNamespace { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs index 960a220f6add1..044aa1402ae46 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/SignatureTestExtensions.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#pragma warning disable CA1801 // Review unused parameters -#pragma warning disable S1118 - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { // test particular method signature variations are generated correctly diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs index 33a5ef073dc6e..840e5bce5cadb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#pragma warning disable CA1801 // Review unused parameters - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class TemplateTestExtensions diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs index 67bde9243dd20..13ed72bbc8276 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TestInstances.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#pragma warning disable CA1801 // Review unused parameters -#pragma warning disable CA1822 - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { public partial class TestInstances From 0cc83167c850c8e4257368d5e9424b999539e2a0 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 06:36:29 -0700 Subject: [PATCH 091/121] Added ActiveIssue for roslyn issue --- .../LoggerMessageGeneratedCodeTests.cs | 10 ++++++++++ .../TestClasses/MessageTestExtensions.cs | 3 +++ 2 files changed, 13 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index aaaf9d6f1a0c1..6db9221beb1eb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -178,8 +178,18 @@ public void MessageTests() Assert.Equal(string.Empty, logger.LastFormattedString); Assert.Equal(LogLevel.Debug, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); + } + [Fact] + [ActiveIssue("https://github.com/dotnet/roslyn/issues/52527")] + public void MessageTests_SuppressWarning_WarnAsError_NoError() + { + // Diagnostics produced by source generators do not respect the /warnAsError or /noWarn compiler flags. + // These are handled fine by the logger generator and generate warnings. Unfortunately, the warning suppression is + // not being observed by the C# compiler at the moment, so having these here causes build warnings. #if false + var logger = new MockLogger(); + logger.Reset(); MessageTestExtensions.M2(logger, "Foo", "Bar"); Assert.Null(logger.LastException); diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index 91ff8b36fbe66..9aa57bb45f635 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable SYSLIB0027 + namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { internal static partial class MessageTestExtensions @@ -12,6 +14,7 @@ internal static partial class MessageTestExtensions public static partial void M1(ILogger logger); #if false + // Diagnostics produced by source generators do not respect the /warnAsError or /noWarn compiler flags. // These are disabled due to https://github.com/dotnet/roslyn/issues/52527 // // These are handled fine by the logger generator and generate warnings. Unfortunately, the above warning suppression is From 1297f1153148f3b40c9eecc87f394226dc2434e3 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 10:22:00 -0700 Subject: [PATCH 092/121] Rename DiagDescriptors to DiagnosticDescriptors --- ...escriptors.cs => DiagnosticDescriptors.cs} | 2 +- .../gen/LoggerMessageGenerator.Parser.cs | 44 ++++++++-------- .../LoggerMessageGeneratorParserTests.cs | 52 +++++++++---------- 3 files changed, 49 insertions(+), 49 deletions(-) rename src/libraries/Microsoft.Extensions.Logging/gen/{DiagDescriptors.cs => DiagnosticDescriptors.cs} (99%) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs similarity index 99% rename from src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs rename to src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs index 2113fa4687328..d151ceacb5ce6 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Logging.Generators { - internal static class DiagDescriptors + internal static class DiagnosticDescriptors { public static DiagnosticDescriptor InvalidLoggingMethodName { get; } = new ( id: "SYSLIB0013", diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 1790cf142a33e..8f8c46b3b204c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -57,21 +57,21 @@ public IReadOnlyList GetLogClasses(IEnumerable(); } var enumerableSymbol = _compilation.GetTypeByMetadataName("System.Collections.IEnumerable"); if (enumerableSymbol == null) { - Diag(DiagDescriptors.MissingRequiredType, null, "System.Collections.IEnumerable"); + Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.Collections.IEnumerable"); return Array.Empty(); } var stringSymbol = _compilation.GetTypeByMetadataName("System.String"); if (stringSymbol == null) { - Diag(DiagDescriptors.MissingRequiredType, null, "System.String"); + Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.String"); return Array.Empty(); } @@ -138,20 +138,20 @@ public IReadOnlyList GetLogClasses(IEnumerable 0) { // we don't currently support generic methods - Diag(DiagDescriptors.LoggingMethodIsGeneric, method.Identifier.GetLocation()); + Diag(DiagnosticDescriptors.LoggingMethodIsGeneric, method.Identifier.GetLocation()); keepMethod = false; } @@ -173,20 +173,20 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable Date: Tue, 13 Apr 2021 12:11:38 -0700 Subject: [PATCH 093/121] - Remove LangVersions from csproj - Remove "_ = " from test project - Remove suppressed warnings in RoslynTestUtils - Use explicit types (not var) in RoslynTestUtils - GetAssembly -> typeof(..).Assembly - Use a globbing pattern to copy TestClasses to output - Remove TreatWarningAsError, on by default - Correct expected assertions for MessageTest --- .../tests/SourceGenerators/RoslynTestUtils.cs | 82 +++++++++---------- ...osoft.Extensions.Logging.Generators.csproj | 1 - .../LoggerMessageGeneratedCodeTests.cs | 14 +++- .../LoggerMessageGeneratorEmitterTests.cs | 4 +- .../LoggerMessageGeneratorParserTests.cs | 50 +++++------ ...Extensions.Logging.Generators.Tests.csproj | 31 +------ .../TestClasses/MessageTestExtensions.cs | 2 +- 7 files changed, 79 insertions(+), 105 deletions(-) diff --git a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs index 139cdf40e7e44..b14572bf49405 100644 --- a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs +++ b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs @@ -28,10 +28,8 @@ internal static class RoslynTestUtils /// Whether to include references to the BCL assemblies. public static Project CreateTestProject(IEnumerable? references, bool includeBaseReferences = true) { -#pragma warning disable SA1009 // Closing parenthesis should be spaced correctly - var corelib = Assembly.GetAssembly(typeof(object))!.Location; - var runtimeDir = Path.GetDirectoryName(corelib)!; -#pragma warning restore SA1009 // Closing parenthesis should be spaced correctly + string corelib = Assembly.GetAssembly(typeof(object))!.Location; + string runtimeDir = Path.GetDirectoryName(corelib)!; var refs = new List(); if (includeBaseReferences) @@ -49,13 +47,11 @@ public static Project CreateTestProject(IEnumerable? references, bool } } -#pragma warning disable CA2000 // Dispose objects before losing scope return new AdhocWorkspace() .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create())) .AddProject("Test", "test.dll", "C#") .WithMetadataReferences(refs) .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithNullableContextOptions(NullableContextOptions.Enable)); -#pragma warning restore CA2000 // Dispose objects before losing scope } public static Task CommitChanges(this Project proj, params string[] ignorables) @@ -66,12 +62,12 @@ public static Task CommitChanges(this Project proj, params string[] ignorables) public static async Task AssertNoDiagnostic(this Project proj, params string[] ignorables) { - foreach (var doc in proj.Documents) + foreach (Document doc in proj.Documents) { - var sm = await doc.GetSemanticModelAsync(CancellationToken.None).ConfigureAwait(false); + SemanticModel sm = await doc.GetSemanticModelAsync(CancellationToken.None).ConfigureAwait(false); Assert.NotNull(sm); - foreach (var d in sm!.GetDiagnostics()) + foreach (Diagnostic d in sm!.GetDiagnostics()) { bool ignore = ignorables.Any(ig => d.Id == ig); @@ -87,7 +83,7 @@ public static Project WithDocument(this Project proj, string name, string text) public static Document FindDocument(this Project proj, string name) { - foreach (var doc in proj.Documents) + foreach (Document doc in proj.Documents) { if (doc.Name == name) { @@ -133,22 +129,22 @@ public static TextSpan MakeSpan(string text, int spanNum) bool includeBaseReferences = true, CancellationToken cancellationToken = default) { - var proj = CreateTestProject(references, includeBaseReferences); + Project proj = CreateTestProject(references, includeBaseReferences); - var count = 0; - foreach (var s in sources) + int count = 0; + foreach (string s in sources) { proj = proj.WithDocument($"src-{count++}.cs", s); } Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution)); - var comp = await proj!.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false); + Compilation comp = await proj!.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false); - var cgd = CSharpGeneratorDriver.Create(new[] { generator }, optionsProvider: optionsProvider); - var gd = cgd.RunGenerators(comp!, cancellationToken); + CSharpGeneratorDriver cgd = CSharpGeneratorDriver.Create(new[] { generator }, optionsProvider: optionsProvider); + GeneratorDriver gd = cgd.RunGenerators(comp!, cancellationToken); - var r = gd.GetRunResult(); + GeneratorDriverRunResult r = gd.GetRunResult(); return (r.Results[0].Diagnostics, r.Results[0].GeneratedSources); } @@ -160,19 +156,19 @@ public static async Task> RunAnalyzer( IEnumerable references, IEnumerable sources) { - var proj = CreateTestProject(references); + Project proj = CreateTestProject(references); - var count = 0; - foreach (var s in sources) + int count = 0; + foreach (string s in sources) { proj = proj.WithDocument($"src-{count++}.cs", s); } await proj.CommitChanges().ConfigureAwait(false); - var analyzers = ImmutableArray.Create(analyzer); + ImmutableArray analyzers = ImmutableArray.Create(analyzer); - var comp = await proj!.GetCompilationAsync().ConfigureAwait(false); + Compilation comp = await proj!.GetCompilationAsync().ConfigureAwait(false); return await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false); } @@ -188,20 +184,20 @@ public static async Task> RunAnalyzerAndFixer( string? defaultNamespace = null, string? extraFile = null) { - var proj = CreateTestProject(references); + Project proj = CreateTestProject(references); - var count = 0; + int count = 0; if (sourceNames != null) { - var l = sourceNames.ToList(); - foreach (var s in sources) + List l = sourceNames.ToList(); + foreach (string s in sources) { proj = proj.WithDocument(l[count++], s); } } else { - foreach (var s in sources) + foreach (string s in sources) { proj = proj.WithDocument($"src-{count++}.cs", s); } @@ -214,12 +210,12 @@ public static async Task> RunAnalyzerAndFixer( await proj.CommitChanges().ConfigureAwait(false); - var analyzers = ImmutableArray.Create(analyzer); + ImmutableArray analyzers = ImmutableArray.Create(analyzer); while (true) { - var comp = await proj!.GetCompilationAsync().ConfigureAwait(false); - var diags = await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false); + Compilation comp = await proj!.GetCompilationAsync().ConfigureAwait(false); + ImmutableArray diags = await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false); if (diags.IsEmpty) { // no more diagnostics reported by the analyzers @@ -227,11 +223,11 @@ public static async Task> RunAnalyzerAndFixer( } var actions = new List(); - foreach (var d in diags) + foreach (Diagnostic d in diags) { - var doc = proj.GetDocument(d.Location.SourceTree); + Document doc = proj.GetDocument(d.Location.SourceTree); - var context = new CodeFixContext(doc!, d, (action, _) => actions.Add(action), CancellationToken.None); + CodeFixContext context = new CodeFixContext(doc!, d, (action, _) => actions.Add(action), CancellationToken.None); await fixer.RegisterCodeFixesAsync(context).ConfigureAwait(false); } @@ -241,9 +237,9 @@ public static async Task> RunAnalyzerAndFixer( break; } - var operations = await actions[0].GetOperationsAsync(CancellationToken.None).ConfigureAwait(false); - var solution = operations.OfType().Single().ChangedSolution; - var changedProj = solution.GetProject(proj.Id); + ImmutableArray operations = await actions[0].GetOperationsAsync(CancellationToken.None).ConfigureAwait(false); + Solution solution = operations.OfType().Single().ChangedSolution; + Project changedProj = solution.GetProject(proj.Id); if (changedProj != proj) { proj = await RecreateProjectDocumentsAsync(changedProj!).ConfigureAwait(false); @@ -254,10 +250,10 @@ public static async Task> RunAnalyzerAndFixer( if (sourceNames != null) { - var l = sourceNames.ToList(); + List l = sourceNames.ToList(); for (int i = 0; i < count; i++) { - var s = await proj.FindDocument(l[i]).GetTextAsync().ConfigureAwait(false); + SourceText s = await proj.FindDocument(l[i]).GetTextAsync().ConfigureAwait(false); results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal)); } } @@ -265,14 +261,14 @@ public static async Task> RunAnalyzerAndFixer( { for (int i = 0; i < count; i++) { - var s = await proj.FindDocument($"src-{i}.cs").GetTextAsync().ConfigureAwait(false); + SourceText s = await proj.FindDocument($"src-{i}.cs").GetTextAsync().ConfigureAwait(false); results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal)); } } if (extraFile != null) { - var s = await proj.FindDocument(extraFile).GetTextAsync().ConfigureAwait(false); + SourceText s = await proj.FindDocument(extraFile).GetTextAsync().ConfigureAwait(false); results.Add(s.ToString().Replace("\r\n", "\n", StringComparison.Ordinal)); } @@ -281,9 +277,9 @@ public static async Task> RunAnalyzerAndFixer( private static async Task RecreateProjectDocumentsAsync(Project project) { - foreach (var documentId in project.DocumentIds) + foreach (DocumentId documentId in project.DocumentIds) { - var document = project.GetDocument(documentId); + Document document = project.GetDocument(documentId); document = await RecreateDocumentAsync(document!).ConfigureAwait(false); project = document.Project; } @@ -293,7 +289,7 @@ private static async Task RecreateProjectDocumentsAsync(Project project private static async Task RecreateDocumentAsync(Document document) { - var newText = await document.GetTextAsync().ConfigureAwait(false); + SourceText newText = await document.GetTextAsync().ConfigureAwait(false); return document.WithText(SourceText.From(newText.ToString(), newText.Encoding, newText.ChecksumAlgorithm)); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 4ada1f2dfe543..a3d903d0361f8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -4,7 +4,6 @@ netstandard2.0 true false - 9.0 true diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 6db9221beb1eb..7e0661d77a699 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -193,14 +193,22 @@ public void MessageTests_SuppressWarning_WarnAsError_NoError() logger.Reset(); MessageTestExtensions.M2(logger, "Foo", "Bar"); Assert.Null(logger.LastException); - Assert.Equal("{\"p1\":\"Foo\",\"p2\":\"Bar\"}", logger.LastFormattedString); + Assert.Equal(string.Empty, logger.LastFormattedString); + AssertLastState(logger, + new KeyValuePair("p1", "Foo"), + new KeyValuePair("p2", "Bar"), + new KeyValuePair("{OriginalFormat}", string.Empty)); Assert.Equal(LogLevel.Trace, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); logger.Reset(); MessageTestExtensions.M3(logger, "Foo", 42); Assert.Null(logger.LastException); - Assert.Equal("{\"p1\":\"Foo\",\"p2\":\"42\"}", logger.LastFormattedString); + Assert.Equal(string.Empty, logger.LastFormattedString); + AssertLastState(logger, + new KeyValuePair("p1", "Foo"), + new KeyValuePair("p2", 42), + new KeyValuePair("{OriginalFormat}", string.Empty)); Assert.Equal(LogLevel.Debug, logger.LastLogLevel); Assert.Equal(1, logger.CallCount); #endif @@ -423,7 +431,7 @@ private static void TestCollection(int expected, MockLogger logger) } Assert.Equal(expected, count); - _ = Assert.Throws(() => _ = rol[expected]); + Assert.Throws(() => rol[expected]); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index f155b03391520..e92af67d12b4f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -23,11 +23,11 @@ public async Task TestEmitter() var (d, r) = await RoslynTestUtils.RunGenerator( new LoggerMessageGenerator(), - new[] { Assembly.GetAssembly(typeof(ILogger))!, Assembly.GetAssembly(typeof(LoggerMessageAttribute))! }, + new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly }, new[] { testSourceCode }).ConfigureAwait(false); Assert.Empty(d); - _ = Assert.Single(r); + Assert.Single(r); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index c0b0a6198ab6f..a917c902b7810 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -26,7 +26,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.InvalidLoggingMethodName.Id, d[0].Id); } @@ -41,7 +41,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.MissingLogLevel.Id, d[0].Id); } @@ -60,7 +60,7 @@ static partial void M1(ILogger logger) } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, d[0].Id); } @@ -75,7 +75,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, d[0].Id); } @@ -90,7 +90,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.TemplateHasNoCorrespondingArgument.Id, d[0].Id); } @@ -105,7 +105,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.RedundantQualifierInMessage.Id, d[0].Id); } @@ -120,7 +120,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.ShouldntMentionExceptionInMessage.Id, d[0].Id); } @@ -135,7 +135,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.ShouldntMentionLogLevelInMessage.Id, d[0].Id); } @@ -150,7 +150,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.ShouldntMentionLoggerInMessage.Id, d[0].Id); } @@ -167,7 +167,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.XXX.Id, d[0].Id); } @@ -183,7 +183,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.XXX.Id, d[0].Id); } @@ -199,7 +199,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.XXX.Id, d[0].Id); } #endif @@ -215,7 +215,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.InvalidLoggingMethodParameterName.Id, d[0].Id); } @@ -233,7 +233,7 @@ public partial class Nested } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.LoggingMethodInNestedType.Id, d[0].Id); } @@ -266,7 +266,7 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, d[0].Id); } @@ -299,7 +299,7 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, d[0].Id); } @@ -329,7 +329,7 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, d[0].Id); } @@ -395,7 +395,7 @@ partial class MyClass } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.ShouldntReuseEventIds.Id, d[0].Id); Assert.Contains("in class MyClass", d[0].GetMessage(), StringComparison.InvariantCulture); } @@ -413,7 +413,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.LoggingMethodMustReturnVoid.Id, d[0].Id); } @@ -428,7 +428,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.MissingLoggerArgument.Id, d[0].Id); } @@ -443,7 +443,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.LoggingMethodShouldBeStatic.Id, d[0].Id); } @@ -458,7 +458,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.MissingLoggerField.Id, d[0].Id); } @@ -476,7 +476,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.MultipleLoggerFields.Id, d[0].Id); } @@ -507,7 +507,7 @@ partial class C } "); - _ = Assert.Single(d); + Assert.Single(d); Assert.Equal(DiagnosticDescriptors.LoggingMethodIsGeneric.Id, d[0].Id); } @@ -547,7 +547,7 @@ partial class C public async Task Cancellation() { await Assert.ThrowsAsync(async () => - _ = await RunGenerator(@" + await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index a9468360e795a..054f53bfed4f0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -3,7 +3,6 @@ $(NetCoreAppCurrent) true - true true @@ -37,37 +36,9 @@ - + PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs index 9aa57bb45f635..a30849288b53b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MessageTestExtensions.cs @@ -15,7 +15,7 @@ internal static partial class MessageTestExtensions #if false // Diagnostics produced by source generators do not respect the /warnAsError or /noWarn compiler flags. - // These are disabled due to https://github.com/dotnet/roslyn/issues/52527 + // Disabled due to https://github.com/dotnet/roslyn/issues/52527 // // These are handled fine by the logger generator and generate warnings. Unfortunately, the above warning suppression is // not being observed by the C# compiler at the moment, so having these here causes build warnings. From 13631d2dd1f8a9ba62a94d9c4b52dc14b0d6c609 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 12:44:20 -0700 Subject: [PATCH 094/121] - Remove Moq - Remove InternalsVisibleTo on M.E.DependencyModel.Tests - Undo add ReferenceOutputAssembly on Logging Generator - Diff in ParserTests: use explicit type (not var), rename some vars - Diff in LoggerMessageGeneratorX files: use explicit type (not var) --- .../gen/LoggerMessageGenerator.Emitter.cs | 72 ++++---- .../gen/LoggerMessageGenerator.Parser.cs | 74 ++++---- .../gen/LoggerMessageGenerator.cs | 4 +- .../LoggerMessageGeneratorParserTests.cs | 174 +++++++++--------- ...Extensions.Logging.Generators.Tests.csproj | 7 +- 5 files changed, 163 insertions(+), 168 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index b9d3d1f9922d3..86515656c2c62 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -23,13 +23,13 @@ internal class Emitter public string Emit(IReadOnlyList logClasses, CancellationToken cancellationToken) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { _ = sb.Append("// \n"); _ = sb.Append("#nullable enable\n"); - foreach (var lc in logClasses) + foreach (LoggerClass lc in logClasses) { cancellationToken.ThrowIfCancellationRequested(); _ = sb.Append(GenType(lc)); @@ -53,7 +53,7 @@ private static string EscapeMessageString(string message) private static bool UseLoggerMessageDefine(LoggerMethod lm) { - var result = + bool result = (lm.RegularParameters.Count <= MaxLoggerMessageDefineArguments) && // more args than LoggerMessage.Define can handle (lm.Level != null) && // dynamic log level, which LoggerMessage.Define can't handle (lm.TemplateList.Count == lm.RegularParameters.Count); // mismatch in template to args, which LoggerMessage.Define can't handle @@ -62,7 +62,7 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm) { // make sure the order of the templates matches the order of the logging method parameter int count = 0; - foreach (var t in lm.TemplateList) + foreach (string t in lm.TemplateList) { if (!t.Equals(lm.RegularParameters[count].Name, StringComparison.OrdinalIgnoreCase)) { @@ -77,10 +77,10 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm) private string GenType(LoggerClass lc) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - foreach (var lm in lc.Methods) + foreach (LoggerMethod lm in lc.Methods) { if (!UseLoggerMessageDefine(lm)) { @@ -174,10 +174,10 @@ public override string ToString() private string GenFields(LoggerMethod lm) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - foreach (var p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) { _ = sb.Append($" private readonly {p.Type} _{p.Name};\n"); } @@ -192,10 +192,10 @@ private string GenFields(LoggerMethod lm) private string GenFieldAssignments(LoggerMethod lm) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - foreach (var p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) { _ = sb.Append($" this._{p.Name} = {p.Name};\n"); } @@ -210,13 +210,13 @@ private string GenFieldAssignments(LoggerMethod lm) private string GenVariableAssignments(LoggerMethod lm) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - foreach (var t in lm.TemplateMap) + foreach (KeyValuePair t in lm.TemplateMap) { int index = 0; - foreach (var p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) { if (t.Key.Equals(p.Name, System.StringComparison.OrdinalIgnoreCase)) { @@ -251,13 +251,13 @@ private string GenVariableAssignments(LoggerMethod lm) private string GenCases(LoggerMethod lm) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - var index = 0; - foreach (var p in lm.RegularParameters) + int index = 0; + foreach (LoggerParameter p in lm.RegularParameters) { - var name = p.Name; + string name = p.Name; if (lm.TemplateMap.ContainsKey(name)) { // take the letter casing from the template @@ -278,10 +278,10 @@ private string GenCases(LoggerMethod lm) private string GenCallbackArguments(LoggerMethod lm) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - foreach (var p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) { _ = sb.Append($"{p.Name}, "); } @@ -296,10 +296,10 @@ private string GenCallbackArguments(LoggerMethod lm) private string GenDefineTypes(LoggerMethod lm, bool brackets) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - foreach (var p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) { if (sb.Length > 0) { @@ -309,7 +309,7 @@ private string GenDefineTypes(LoggerMethod lm, bool brackets) _ = sb.Append($"{p.Type}"); } - var result = sb.ToString(); + string result = sb.ToString(); if (!string.IsNullOrEmpty(result)) { if (brackets) @@ -332,10 +332,10 @@ private string GenDefineTypes(LoggerMethod lm, bool brackets) private string GenParameters(LoggerMethod lm) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - foreach (var p in lm.AllParameters) + foreach (LoggerParameter p in lm.AllParameters) { if (sb.Length > 0) { @@ -355,10 +355,10 @@ private string GenParameters(LoggerMethod lm) private string GenArguments(LoggerMethod lm) { - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { - foreach (var p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) { if (sb.Length > 0) { @@ -378,13 +378,13 @@ private string GenArguments(LoggerMethod lm) private string GenHolder(LoggerMethod lm) { - var typeName = $"__{lm.Name}Struct"; + string typeName = $"__{lm.Name}Struct"; - var sb = GetStringBuilder(); + StringBuilder sb = GetStringBuilder(); try { _ = sb.Append($"new {typeName}("); - foreach (var p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) { if (p != lm.RegularParameters[0]) { @@ -410,7 +410,7 @@ private string GenLogMethod(LoggerMethod lm) if (lm.Level == null) { - foreach (var p in lm.AllParameters) + foreach (LoggerParameter p in lm.AllParameters) { if (p.IsLogLevel) { @@ -445,7 +445,7 @@ private string GenLogMethod(LoggerMethod lm) } string exceptionArg = "null"; - foreach (var p in lm.AllParameters) + foreach (LoggerParameter p in lm.AllParameters) { if (p.IsException) { @@ -455,7 +455,7 @@ private string GenLogMethod(LoggerMethod lm) } string logger = lm.LoggerField; - foreach (var p in lm.AllParameters) + foreach (LoggerParameter p in lm.AllParameters) { if (p.IsLogger) { @@ -464,7 +464,7 @@ private string GenLogMethod(LoggerMethod lm) } } - var extension = (lm.IsExtensionMethod ? "this " : string.Empty); + string extension = (lm.IsExtensionMethod ? "this " : string.Empty); if (UseLoggerMessageDefine(lm)) { @@ -505,11 +505,11 @@ private string GenLogMethod(LoggerMethod lm) private string GenEnumerationHelper(LoggerClass lc) { - foreach (var lm in lc.Methods) + foreach (LoggerMethod lm in lc.Methods) { if (UseLoggerMessageDefine(lm)) { - foreach (var p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) { if (p.IsEnumerable) { @@ -575,7 +575,7 @@ private StringBuilder GetStringBuilder() return new StringBuilder(DefaultStringBuilderCapacity); } - var sb = _builders.Pop(); + StringBuilder sb = _builders.Pop(); _ = sb.Clear(); return sb; } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 8f8c46b3b204c..0979231fd5e28 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -33,42 +33,42 @@ public IReadOnlyList GetLogClasses(IEnumerable(); } - var loggerSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILogger"); + INamedTypeSymbol loggerSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILogger"); if (loggerSymbol == null) { // nothing to do if this type isn't available return Array.Empty(); } - var logLevelSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LogLevel"); + INamedTypeSymbol logLevelSymbol = _compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.LogLevel"); if (logLevelSymbol == null) { // nothing to do if this type isn't available return Array.Empty(); } - var exceptionSymbol = _compilation.GetTypeByMetadataName("System.Exception"); + INamedTypeSymbol exceptionSymbol = _compilation.GetTypeByMetadataName("System.Exception"); if (exceptionSymbol == null) { Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.Exception"); return Array.Empty(); } - var enumerableSymbol = _compilation.GetTypeByMetadataName("System.Collections.IEnumerable"); + INamedTypeSymbol enumerableSymbol = _compilation.GetTypeByMetadataName("System.Collections.IEnumerable"); if (enumerableSymbol == null) { Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.Collections.IEnumerable"); return Array.Empty(); } - var stringSymbol = _compilation.GetTypeByMetadataName("System.String"); + INamedTypeSymbol stringSymbol = _compilation.GetTypeByMetadataName("System.String"); if (stringSymbol == null) { Diag(DiagnosticDescriptors.MissingRequiredType, null, "System.String"); @@ -82,7 +82,7 @@ public IReadOnlyList GetLogClasses(IEnumerable x.SyntaxTree)) { SemanticModel? sm = null; - foreach (var classDec in group) + foreach (ClassDeclarationSyntax classDec in group) { // stop if we're asked to _cancellationToken.ThrowIfCancellationRequested(); @@ -102,22 +102,22 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable t in lm.TemplateMap) { bool found = false; - foreach (var p in lm.AllParameters) + foreach (LoggerParameter p in lm.AllParameters) { if (t.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase)) { @@ -392,11 +392,11 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable GetLogClasses(IEnumerable logClasses = p.GetLogClasses(receiver.ClassDeclarations); + string result = e.Emit(logClasses, context.CancellationToken); context.AddSource(nameof(LoggerMessageGenerator), SourceText.From(result, Encoding.UTF8)); } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index a917c902b7810..32cf6283606be 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -18,7 +18,7 @@ public class LoggerMessageGeneratorParserTests [Fact] public async Task InvalidMethodName() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] @@ -26,14 +26,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.InvalidLoggingMethodName.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.InvalidLoggingMethodName.Id, diagnostics[0].Id); } [Fact] public async Task MissingLogLevel() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Message = ""M1"")] @@ -41,14 +41,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.MissingLogLevel.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.MissingLogLevel.Id, diagnostics[0].Id); } [Fact] public async Task InvalidMethodBody() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { static partial void M1(ILogger logger); @@ -60,14 +60,14 @@ static partial void M1(ILogger logger) } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, diagnostics[0].Id); } [Fact] public async Task MissingTemplate() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""This is a message without foo"")] @@ -75,14 +75,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id); } [Fact] public async Task MissingArgument() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""{foo}"")] @@ -90,14 +90,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.TemplateHasNoCorrespondingArgument.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.TemplateHasNoCorrespondingArgument.Id, diagnostics[0].Id); } [Fact] public async Task NeedlessQualifierInMessage() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = ""INFO: this is an informative message"")] @@ -105,14 +105,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.RedundantQualifierInMessage.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.RedundantQualifierInMessage.Id, diagnostics[0].Id); } [Fact] public async Task NeedlessExceptionInMessage() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {ex} {ex2}"")] @@ -120,14 +120,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.ShouldntMentionExceptionInMessage.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.ShouldntMentionExceptionInMessage.Id, diagnostics[0].Id); } [Fact] public async Task NeedlessLogLevelInMessage() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Message = ""M1 {l1} {l2}"")] @@ -135,14 +135,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.ShouldntMentionLogLevelInMessage.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.ShouldntMentionLogLevelInMessage.Id, diagnostics[0].Id); } [Fact] public async Task NeedlessLoggerInMessage() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {logger}"")] @@ -150,8 +150,8 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.ShouldntMentionLoggerInMessage.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.ShouldntMentionLoggerInMessage.Id, diagnostics[0].Id); } #if false @@ -159,7 +159,7 @@ partial class C [Fact] public async Task DoubleLogLevel() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] @@ -167,15 +167,15 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.XXX.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.XXX.Id, diagnostics[0].Id); } // TODO: can't have the same template with different casing [Fact] public async Task InconsistentTemplateCasing() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {p1} {P1}"")] @@ -183,15 +183,15 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.XXX.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.XXX.Id, diagnostics[0].Id); } // TODO: can't have malformed format strings (like dangling {, etc) [Fact] public async Task MalformedFormatString() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {p1} {P1}"")] @@ -199,15 +199,15 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.XXX.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.XXX.Id, diagnostics[0].Id); } #endif [Fact] public async Task InvalidParameterName() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {__foo}"")] @@ -215,14 +215,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.InvalidLoggingMethodParameterName.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.InvalidLoggingMethodParameterName.Id, diagnostics[0].Id); } [Fact] public async Task NestedType() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { public partial class Nested @@ -233,14 +233,14 @@ public partial class Nested } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.LoggingMethodInNestedType.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.LoggingMethodInNestedType.Id, diagnostics[0].Id); } [Fact] public async Task MissingExceptionType() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" namespace System { public class Object {} @@ -266,14 +266,14 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, diagnostics[0].Id); } [Fact] public async Task MissingStringType() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" namespace System { public class Object {} @@ -299,14 +299,14 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, diagnostics[0].Id); } [Fact] public async Task MissingEnumerableType() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" namespace System { public class Object {} @@ -329,26 +329,26 @@ partial class C } ", false, includeBaseReferences: false, includeLoggingReferences: false); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.MissingRequiredType.Id, diagnostics[0].Id); } [Fact] public async Task MissingLoggerMessageAttributeType() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { } ", false, includeLoggingReferences: false); - Assert.Empty(d); + Assert.Empty(diagnostics); } [Fact] public async Task MissingILoggerType() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" namespace Microsoft.Extensions.Logging { public sealed class LoggerMessageAttribute : System.Attribute {} @@ -358,13 +358,13 @@ partial class C } ", false, includeLoggingReferences: false); - Assert.Empty(d); + Assert.Empty(diagnostics); } [Fact] public async Task MissingLogLevelType() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" namespace Microsoft.Extensions.Logging { public sealed class LoggerMessageAttribute : System.Attribute {} @@ -378,13 +378,13 @@ partial class C } ", false, includeLoggingReferences: false); - Assert.Empty(d); + Assert.Empty(diagnostics); } [Fact] public async Task EventIdReuse() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class MyClass { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] @@ -395,15 +395,15 @@ partial class MyClass } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.ShouldntReuseEventIds.Id, d[0].Id); - Assert.Contains("in class MyClass", d[0].GetMessage(), StringComparison.InvariantCulture); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.ShouldntReuseEventIds.Id, diagnostics[0].Id); + Assert.Contains("in class MyClass", diagnostics[0].GetMessage(), StringComparison.InvariantCulture); } [Fact] public async Task MethodReturnType() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] @@ -413,14 +413,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.LoggingMethodMustReturnVoid.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.LoggingMethodMustReturnVoid.Id, diagnostics[0].Id); } [Fact] public async Task MissingILogger() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {p1}"")] @@ -428,14 +428,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.MissingLoggerArgument.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.MissingLoggerArgument.Id, diagnostics[0].Id); } [Fact] public async Task NotStatic() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] @@ -443,14 +443,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.LoggingMethodShouldBeStatic.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.LoggingMethodShouldBeStatic.Id, diagnostics[0].Id); } [Fact] public async Task NoILoggerField() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] @@ -458,14 +458,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.MissingLoggerField.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.MissingLoggerField.Id, diagnostics[0].Id); } [Fact] public async Task MultipleILoggerFields() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { public ILogger _logger1; @@ -476,14 +476,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.MultipleLoggerFields.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.MultipleLoggerFields.Id, diagnostics[0].Id); } [Fact] public async Task NotPartial() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] @@ -491,15 +491,15 @@ static void M1(ILogger logger) {} } "); - Assert.Equal(2, d.Count); - Assert.Equal(DiagnosticDescriptors.LoggingMethodMustBePartial.Id, d[0].Id); - Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, d[1].Id); + Assert.Equal(2, diagnostics.Count); + Assert.Equal(DiagnosticDescriptors.LoggingMethodMustBePartial.Id, diagnostics[0].Id); + Assert.Equal(DiagnosticDescriptors.LoggingMethodHasBody.Id, diagnostics[1].Id); } [Fact] public async Task MethodGeneric() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] @@ -507,14 +507,14 @@ partial class C } "); - Assert.Single(d); - Assert.Equal(DiagnosticDescriptors.LoggingMethodIsGeneric.Id, d[0].Id); + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.LoggingMethodIsGeneric.Id, diagnostics[0].Id); } [Fact] public async Task Templates() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = ""M1"")] @@ -540,7 +540,7 @@ partial class C } "); - Assert.Empty(d); + Assert.Empty(diagnostics); } [Fact] @@ -559,7 +559,7 @@ partial class C [Fact] public async Task SourceErrors() { - var d = await RunGenerator(@" + IReadOnlyList diagnostics = await RunGenerator(@" static partial class C { // bogus argument type @@ -580,7 +580,7 @@ static partial class C } "); - Assert.Empty(d); // should fail quietly on broken code + Assert.Empty(diagnostics); // should fail quietly on broken code } private static async Task> RunGenerator( diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 054f53bfed4f0..8c381c35b51ca 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -11,16 +11,11 @@ Link="SourceGenerators\RoslynTestUtils.cs" /> - - - - - @@ -32,7 +27,7 @@ - + From d9970f7778a52917dc6ba4f44dcf06af8247d82b Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 12:57:45 -0700 Subject: [PATCH 095/121] - Skip on browser Cant load Microsoft.CodeAnalysis - Remove from gen csproj: RestoreAdditionalProjectSources - Remove unused `LoggerField = "_logger";` instantiation --- .../gen/LoggerMessageGenerator.Parser.cs | 2 +- .../gen/Microsoft.Extensions.Logging.Generators.csproj | 4 ---- .../AssemblyInfo.cs | 6 ++++++ 3 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/AssemblyInfo.cs diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 0979231fd5e28..a1bf8b8ea9d46 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -635,7 +635,7 @@ internal class LoggerMethod public string? EventName; public bool IsExtensionMethod; public string Modifiers = string.Empty; - public string LoggerField = "_logger"; + public string LoggerField; } /// diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index a3d903d0361f8..9c9393b79917d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -7,10 +7,6 @@ true - - https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json ;$(RestoreAdditionalProjectSources) - - diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/AssemblyInfo.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/AssemblyInfo.cs new file mode 100644 index 0000000000000..073832b5e40e7 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +[assembly: SkipOnPlatform(TestPlatforms.Browser, "Microsoft.Extensions.Logging.Generator is not supported on Browser.")] \ No newline at end of file From d809dad73dd9d14ebc095e70dfde08b8773ce483 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 14:29:05 -0700 Subject: [PATCH 096/121] - Add CLSCompliant on gen csproj - Dont need Microsoft.CodeAnalysis.Analyzers for gen csproj - Dont need InternalsVisibleTo for gen in test csproj - Remove many unnecessary p2p refs in test csproj - Remove FixIncrementalCoreCompileWithAnalyzers target --- eng/Versions.props | 1 - .../gen/DiagnosticDescriptors.cs | 2 +- .../gen/LoggerMessageGenerator.cs | 2 -- ...osoft.Extensions.Logging.Generators.csproj | 2 +- .../gen/Properties/InternalsVisibleTo.cs | 6 ------ ...Extensions.Logging.Generators.Tests.csproj | 21 +------------------ 6 files changed, 3 insertions(+), 31 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Logging/gen/Properties/InternalsVisibleTo.cs diff --git a/eng/Versions.props b/eng/Versions.props index d5662cec61808..e14f32158ef91 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,7 +45,6 @@ - 3.3.1 6.0.0-preview3.21168.1 3.9.0-5.final 3.9.0-5.final diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs index d151ceacb5ce6..fe445ac36a0c2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Logging.Generators { - internal static class DiagnosticDescriptors + public static class DiagnosticDescriptors { public static DiagnosticDescriptor InvalidLoggingMethodName { get; } = new ( id: "SYSLIB0013", diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs index 25ce0d66c3cc8..0c2f42ea4562e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs @@ -18,14 +18,12 @@ namespace Microsoft.Extensions.Logging.Generators public partial class LoggerMessageGenerator : ISourceGenerator { [ExcludeFromCodeCoverage] - [CLSCompliant(false)] public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(SyntaxReceiver.Create); } [ExcludeFromCodeCoverage] - [CLSCompliant(false)] public void Execute(GeneratorExecutionContext context) { var receiver = context.SyntaxReceiver as SyntaxReceiver; diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 9c9393b79917d..255074a0a8c88 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -5,6 +5,7 @@ true false true + false @@ -13,7 +14,6 @@ - diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Properties/InternalsVisibleTo.cs b/src/libraries/Microsoft.Extensions.Logging/gen/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 3b7398f6dd91f..0000000000000 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.Extensions.Logging.Generators.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 8c381c35b51ca..265e08f06add0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -12,21 +12,8 @@ + - - - - - - - - - - - - - - @@ -40,10 +27,4 @@ - - - - - - From dd51c531f93acd9901083b3ae6b484f3d6c60a41 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 17:41:55 -0700 Subject: [PATCH 097/121] - Add record of SYSLIBXXXX in the md file - Change SYSLIBXXXX IDs to unique for analyzers - Move MicrosoftCodeAnalysisCSharpWorkspacesVersion in versions.props - Set generator test as ProjectExclusions when TargetsMono is true - Remove AssemblyInfo.cs not needed --- docs/project/list-of-obsoletions.md | 26 +++++++++++- eng/Versions.props | 5 ++- .../gen/DiagnosticDescriptors.cs | 40 +++++++++---------- .../AssemblyInfo.cs | 6 --- src/libraries/tests.proj | 5 +++ 5 files changed, 54 insertions(+), 28 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/AssemblyInfo.cs diff --git a/docs/project/list-of-obsoletions.md b/docs/project/list-of-obsoletions.md index cb094673111ec..feb0568ea6097 100644 --- a/docs/project/list-of-obsoletions.md +++ b/docs/project/list-of-obsoletions.md @@ -26,4 +26,28 @@ Currently the identifiers `SYSLIB0001` through `SYSLIB0999` are carved out for o | __`SYSLIB0011`__ | `BinaryFormatter` serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for recommended alternatives. | | __`SYSLIB0012`__ | Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location instead. | | __`SYSLIB0013`__ | Uri.EscapeUriString can corrupt the Uri string in some cases. Consider using Uri.EscapeDataString for query string components instead. | -| __`SYSLIB0015`__ | DisablePrivateReflectionAttribute has no effect in .NET 6.0+ applications. | \ No newline at end of file +| __`SYSLIB0015`__ | DisablePrivateReflectionAttribute has no effect in .NET 6.0+ applications. | + +### Analyzer warnings (`SYSLIB1001` - `SYSLIB1999`) +| Diagnostic ID | Description | +| :---------------- | :---------- | +| __`SYSLIB1001`__ | Logging method names cannot start with _ | +| __`SYSLIB1002`__ | Don't include log level parameters as templates in the logging message | +| __`SYSLIB1003`__ | InvalidLoggingMethodParameterNameTitle | +| __`SYSLIB1004`__ | Logging class cannot be in nested types | +| __`SYSLIB1005`__ | Could not find a required type definition | +| __`SYSLIB1006`__ | Multiple logging methods cannot use the same event id within a class | +| __`SYSLIB1007`__ | Logging methods must return void | +| __`SYSLIB1008`__ | One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface | +| __`SYSLIB1009`__ | Logging methods must be static | +| __`SYSLIB1010`__ | Logging methods must be partial | +| __`SYSLIB1011`__ | Logging methods cannot be generic | +| __`SYSLIB1012`__ | Redundant qualifier in logging message | +| __`SYSLIB1013`__ | Don't include exception parameters as templates in the logging message | +| __`SYSLIB1014`__ | Logging template has no corresponding method argument | +| __`SYSLIB1015`__ | Argument is not referenced from the logging message | +| __`SYSLIB1016`__ | Logging methods cannot have a body | +| __`SYSLIB1017`__ | A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method | +| __`SYSLIB1018`__ | Don't include logger parameters as templates in the logging message | +| __`SYSLIB1019`__ | Couldn't find a field of type Microsoft.Extensions.Logging.ILogger | +| __`SYSLIB1020`__ | Found multiple fields of type Microsoft.Extensions.Logging.ILogger | \ No newline at end of file diff --git a/eng/Versions.props b/eng/Versions.props index e14f32158ef91..320f4b75b8589 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -44,11 +44,14 @@ + + + 3.8.0 + 6.0.0-preview3.21168.1 3.9.0-5.final 3.9.0-5.final - 3.8.0 6.0.0-beta.21207.4 6.0.0-beta.21207.4 diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs index fe445ac36a0c2..84ecdd81ca822 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.Generators public static class DiagnosticDescriptors { public static DiagnosticDescriptor InvalidLoggingMethodName { get; } = new ( - id: "SYSLIB0013", + id: "SYSLIB1001", title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -17,7 +17,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor ShouldntMentionLogLevelInMessage { get; } = new ( - id: "SYSLIB0014", + id: "SYSLIB1002", title: new LocalizableResourceString(nameof(SR.ShouldntMentionLogLevelInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionLogLevelInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -25,7 +25,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor InvalidLoggingMethodParameterName { get; } = new ( - id: "SYSLIB0015", + id: "SYSLIB1003", title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -33,7 +33,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor LoggingMethodInNestedType { get; } = new ( - id: "SYSLIB0016", + id: "SYSLIB1004", title: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -41,7 +41,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor MissingRequiredType { get; } = new ( - id: "SYSLIB0017", + id: "SYSLIB1005", title: new LocalizableResourceString(nameof(SR.MissingRequiredTypeTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingRequiredTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -49,7 +49,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor ShouldntReuseEventIds { get; } = new ( - id: "SYSLIB0018", + id: "SYSLIB1006", title: new LocalizableResourceString(nameof(SR.ShouldntReuseEventIdsTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ShouldntReuseEventIdsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -57,7 +57,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor LoggingMethodMustReturnVoid { get; } = new ( - id: "SYSLIB0019", + id: "SYSLIB1007", title: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -65,7 +65,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor MissingLoggerArgument { get; } = new ( - id: "SYSLIB0020", + id: "SYSLIB1008", title: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -73,7 +73,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor LoggingMethodShouldBeStatic { get; } = new ( - id: "SYSLIB0021", + id: "SYSLIB1009", title: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -81,7 +81,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor LoggingMethodMustBePartial { get; } = new ( - id: "SYSLIB0022", + id: "SYSLIB1010", title: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -89,7 +89,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor LoggingMethodIsGeneric { get; } = new ( - id: "SYSLIB0023", + id: "SYSLIB1011", title: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -97,7 +97,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor RedundantQualifierInMessage { get; } = new ( - id: "SYSLIB0024", + id: "SYSLIB1012", title: new LocalizableResourceString(nameof(SR.RedundantQualifierInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.RedundantQualifierInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -105,7 +105,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor ShouldntMentionExceptionInMessage { get; } = new ( - id: "SYSLIB0025", + id: "SYSLIB1013", title: new LocalizableResourceString(nameof(SR.ShouldntMentionExceptionInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionExceptionInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -113,7 +113,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor TemplateHasNoCorrespondingArgument { get; } = new ( - id: "SYSLIB0026", + id: "SYSLIB1014", title: new LocalizableResourceString(nameof(SR.TemplateHasNoCorrespondingArgumentTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.TemplateHasNoCorrespondingArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -121,7 +121,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor ArgumentHasNoCorrespondingTemplate { get; } = new ( - id: "SYSLIB0027", + id: "SYSLIB1015", title: new LocalizableResourceString(nameof(SR.ArgumentHasNoCorrespondingTemplateTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ArgumentHasNoCorrespondingTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -129,7 +129,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor LoggingMethodHasBody { get; } = new ( - id: "SYSLIB0028", + id: "SYSLIB1016", title: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -137,7 +137,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor MissingLogLevel { get; } = new ( - id: "SYSLIB0029", + id: "SYSLIB1017", title: new LocalizableResourceString(nameof(SR.MissingLogLevelTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingLogLevelMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -145,7 +145,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor ShouldntMentionLoggerInMessage { get; } = new ( - id: "SYSLIB0030", + id: "SYSLIB1018", title: new LocalizableResourceString(nameof(SR.ShouldntMentionLoggerInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionLoggerInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -153,7 +153,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor MissingLoggerField { get; } = new ( - id: "SYSLIB0031", + id: "SYSLIB1019", title: new LocalizableResourceString(nameof(SR.MissingLoggerFieldTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingLoggerFieldMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", @@ -161,7 +161,7 @@ public static class DiagnosticDescriptors isEnabledByDefault: true); public static DiagnosticDescriptor MultipleLoggerFields { get; } = new( - id: "SYSLIB0032", + id: "SYSLIB1020", title: new LocalizableResourceString(nameof(SR.MultipleLoggerFieldsTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MultipleLoggerFieldsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/AssemblyInfo.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/AssemblyInfo.cs deleted file mode 100644 index 073832b5e40e7..0000000000000 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Xunit; - -[assembly: SkipOnPlatform(TestPlatforms.Browser, "Microsoft.Extensions.Logging.Generator is not supported on Browser.")] \ No newline at end of file diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index e894ba1c091b0..dd9618608d408 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -13,6 +13,11 @@ false + + + + + From 70f24ef85c8dcbd086375371c8140307d0a194a7 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 19:00:57 -0700 Subject: [PATCH 098/121] - Rename md file to list-of-diagnostics.md - "_ = sb" -> "sb" in Emitter.cs - Remove dead code in test csproj - Add MicrosoftCodeAnalysisVersion to versions.props --- ...-obsoletions.md => list-of-diagnostics.md} | 0 eng/Versions.props | 1 + .../gen/LoggerMessageGenerator.Emitter.cs | 48 +++++++++---------- ...Extensions.Logging.Generators.Tests.csproj | 6 +-- 4 files changed, 26 insertions(+), 29 deletions(-) rename docs/project/{list-of-obsoletions.md => list-of-diagnostics.md} (100%) diff --git a/docs/project/list-of-obsoletions.md b/docs/project/list-of-diagnostics.md similarity index 100% rename from docs/project/list-of-obsoletions.md rename to docs/project/list-of-diagnostics.md diff --git a/eng/Versions.props b/eng/Versions.props index 320f4b75b8589..c13010725ce84 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -47,6 +47,7 @@ 3.8.0 + 3.8.0 6.0.0-preview3.21168.1 diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 86515656c2c62..f8c57454724ef 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -26,13 +26,13 @@ public string Emit(IReadOnlyList logClasses, CancellationToken canc StringBuilder sb = GetStringBuilder(); try { - _ = sb.Append("// \n"); - _ = sb.Append("#nullable enable\n"); + sb.Append("// \n"); + sb.Append("#nullable enable\n"); foreach (LoggerClass lc in logClasses) { cancellationToken.ThrowIfCancellationRequested(); - _ = sb.Append(GenType(lc)); + sb.Append(GenType(lc)); } return sb.ToString(); @@ -84,13 +84,13 @@ private string GenType(LoggerClass lc) { if (!UseLoggerMessageDefine(lm)) { - _ = sb.Append(GenStruct(lm)); + sb.Append(GenStruct(lm)); } - _ = sb.Append(GenLogMethod(lm)); + sb.Append(GenLogMethod(lm)); } - _ = sb.Append(GenEnumerationHelper(lc)); + sb.Append(GenEnumerationHelper(lc)); if (string.IsNullOrWhiteSpace(lc.Namespace)) { @@ -179,7 +179,7 @@ private string GenFields(LoggerMethod lm) { foreach (LoggerParameter p in lm.RegularParameters) { - _ = sb.Append($" private readonly {p.Type} _{p.Name};\n"); + sb.Append($" private readonly {p.Type} _{p.Name};\n"); } return sb.ToString(); @@ -197,7 +197,7 @@ private string GenFieldAssignments(LoggerMethod lm) { foreach (LoggerParameter p in lm.RegularParameters) { - _ = sb.Append($" this._{p.Name} = {p.Name};\n"); + sb.Append($" this._{p.Name} = {p.Name};\n"); } return sb.ToString(); @@ -231,12 +231,12 @@ private string GenVariableAssignments(LoggerMethod lm) { if (lm.RegularParameters[index].IsEnumerable) { - _ = sb.Append($" var {t.Key} = " + sb.Append($" var {t.Key} = " + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.RegularParameters[index].Name});\n"); } else { - _ = sb.Append($" var {t.Key} = this._{lm.RegularParameters[index].Name};\n"); + sb.Append($" var {t.Key} = this._{lm.RegularParameters[index].Name};\n"); } } } @@ -264,10 +264,10 @@ private string GenCases(LoggerMethod lm) name = lm.TemplateMap[name]; } - _ = sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),\n"); + sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),\n"); } - _ = sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),\n"); + sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),\n"); return sb.ToString(); } finally @@ -283,7 +283,7 @@ private string GenCallbackArguments(LoggerMethod lm) { foreach (LoggerParameter p in lm.RegularParameters) { - _ = sb.Append($"{p.Name}, "); + sb.Append($"{p.Name}, "); } return sb.ToString(); @@ -303,10 +303,10 @@ private string GenDefineTypes(LoggerMethod lm, bool brackets) { if (sb.Length > 0) { - _ = sb.Append(", "); + sb.Append(", "); } - _ = sb.Append($"{p.Type}"); + sb.Append($"{p.Type}"); } string result = sb.ToString(); @@ -339,10 +339,10 @@ private string GenParameters(LoggerMethod lm) { if (sb.Length > 0) { - _ = sb.Append(", "); + sb.Append(", "); } - _ = sb.Append($"{p.Type} {p.Name}"); + sb.Append($"{p.Type} {p.Name}"); } return sb.ToString(); @@ -362,10 +362,10 @@ private string GenArguments(LoggerMethod lm) { if (sb.Length > 0) { - _ = sb.Append(", "); + sb.Append(", "); } - _ = sb.Append($"{p.Type} {p.Name}"); + sb.Append($"{p.Type} {p.Name}"); } return sb.ToString(); @@ -383,18 +383,18 @@ private string GenHolder(LoggerMethod lm) StringBuilder sb = GetStringBuilder(); try { - _ = sb.Append($"new {typeName}("); + sb.Append($"new {typeName}("); foreach (LoggerParameter p in lm.RegularParameters) { if (p != lm.RegularParameters[0]) { - _ = sb.Append(", "); + sb.Append(", "); } - _ = sb.Append(p.Name); + sb.Append(p.Name); } - _ = sb.Append(')'); + sb.Append(')'); return sb.ToString(); } @@ -576,7 +576,7 @@ private StringBuilder GetStringBuilder() } StringBuilder sb = _builders.Pop(); - _ = sb.Clear(); + sb.Clear(); return sb; } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 265e08f06add0..240666e52f745 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -13,7 +13,7 @@ - + @@ -23,8 +23,4 @@ - - - - From afb43d70cd1abbfe4c03dfa1f525b5994fff13a5 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Tue, 13 Apr 2021 23:22:22 -0700 Subject: [PATCH 099/121] - Add a set of baseline files to test against --- .../TestWithDynamicLogLevel.generated.txt | 62 ++++++++++++ .../TestWithMoreThan6Params.generated.txt | 95 +++++++++++++++++++ .../Baselines/TestWithOneParam.generated.txt | 24 +++++ .../LoggerMessageGeneratorEmitterTests.cs | 75 +++++++++++++++ ...Extensions.Logging.Generators.Tests.csproj | 12 +++ 5 files changed, 268 insertions(+) create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt create mode 100644 src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt new file mode 100644 index 0000000000000..96a3ca78a9e69 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt @@ -0,0 +1,62 @@ +// +#nullable enable + + namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses + { + partial class TestWithDynamicLogLevel + { + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private readonly struct __M9Struct : global::System.Collections.Generic.IReadOnlyList> + { + + + + public override string ToString() + { + + return $"M9"; + } + + public static string Format(__M9Struct state, global::System.Exception? ex) => state.ToString(); + + public int Count => 1; + + public global::System.Collections.Generic.KeyValuePair this[int index] + { + get => index switch + { + 0 => new global::System.Collections.Generic.KeyValuePair("{OriginalFormat}", "M9"), + + _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case + }; + } + + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + { + for (int i = 0; i < 1; i++) + { + yield return this[i]; + } + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void M9(Microsoft.Extensions.Logging.LogLevel level, Microsoft.Extensions.Logging.ILogger logger) + { + if (logger.IsEnabled(level)) + { + logger.Log( + level, + new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)), + new __M9Struct(), + null, + __M9Struct.Format); + } + } + + } + } + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt new file mode 100644 index 0000000000000..37d44ae2fe704 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt @@ -0,0 +1,95 @@ +// +#nullable enable + + namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses + { + partial class TestWithMoreThan6Params + { + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private readonly struct __Method9Struct : global::System.Collections.Generic.IReadOnlyList> + { + private readonly int _p1; + private readonly int _p2; + private readonly int _p3; + private readonly int _p4; + private readonly int _p5; + private readonly int _p6; + private readonly int _p7; + + + public __Method9Struct(int p1, int p2, int p3, int p4, int p5, int p6, int p7) + { + this._p1 = p1; + this._p2 = p2; + this._p3 = p3; + this._p4 = p4; + this._p5 = p5; + this._p6 = p6; + this._p7 = p7; + + } + + + public override string ToString() + { + var p1 = this._p1; + var p2 = this._p2; + var p3 = this._p3; + var p4 = this._p4; + var p5 = this._p5; + var p6 = this._p6; + var p7 = this._p7; + + return $"M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"; + } + + public static string Format(__Method9Struct state, global::System.Exception? ex) => state.ToString(); + + public int Count => 8; + + public global::System.Collections.Generic.KeyValuePair this[int index] + { + get => index switch + { + 0 => new global::System.Collections.Generic.KeyValuePair("p1", this._p1), + 1 => new global::System.Collections.Generic.KeyValuePair("p2", this._p2), + 2 => new global::System.Collections.Generic.KeyValuePair("p3", this._p3), + 3 => new global::System.Collections.Generic.KeyValuePair("p4", this._p4), + 4 => new global::System.Collections.Generic.KeyValuePair("p5", this._p5), + 5 => new global::System.Collections.Generic.KeyValuePair("p6", this._p6), + 6 => new global::System.Collections.Generic.KeyValuePair("p7", this._p7), + 7 => new global::System.Collections.Generic.KeyValuePair("{OriginalFormat}", "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"), + + _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case + }; + } + + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + { + for (int i = 0; i < 8; i++) + { + yield return this[i]; + } + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void Method9(Microsoft.Extensions.Logging.ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7) + { + if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error)) + { + logger.Log( + global::Microsoft.Extensions.Logging.LogLevel.Error, + new global::Microsoft.Extensions.Logging.EventId(8, nameof(Method9)), + new __Method9Struct(p1, p2, p3, p4, p5, p6, p7), + null, + __Method9Struct.Format); + } + } + + } + } + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt new file mode 100644 index 0000000000000..d4cce82b3660d --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt @@ -0,0 +1,24 @@ +// +#nullable enable + + namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses + { + partial class TestWithOneParam + { + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private static readonly global::System.Action __M0Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {A1}"); + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void M0(Microsoft.Extensions.Logging.ILogger logger, int a1) + { + if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error)) + { + __M0Callback(logger, a1, null); + } + } + + } + } + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index e92af67d12b4f..b68036e7e0f7e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -4,6 +4,8 @@ using System.IO; using System.Reflection; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; using SourceGenerators.Tests; using Xunit; @@ -30,5 +32,78 @@ public async Task TestEmitter() Assert.Single(r); } } + + [Fact] + public async Task TestBaseline_TestWithOneParam_Success() + { + string testSourceCode = @" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal static partial class TestWithOneParam + { + [LoggerMessage(EventId = 0, Level = LogLevel.Error, Message = ""M0 {A1}"")] + public static partial void M0(ILogger logger, int a1); + } +}"; + string expectedResult = await File.ReadAllTextAsync(Path.Combine("Baselines", "TestWithOneParam.generated.txt")).ConfigureAwait(false); + + var (d, r) = await RoslynTestUtils.RunGenerator( + new LoggerMessageGenerator(), + new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly }, + new[] { testSourceCode }).ConfigureAwait(false); + + Assert.Empty(d); + Assert.Single(r); + Assert.Equal(expectedResult, r[0].SourceText.ToString()); + } + + [Fact] + public async Task TestBaseline_TestWithMoreThan6Params_Success() + { + // TODO: Remove support for more than 6 arguments + string testSourceCode = @" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal static partial class TestWithMoreThan6Params + { + [LoggerMessage(EventId = 8, Level = LogLevel.Error, Message = ""M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"")] + public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); + } +}"; + string expectedResult = await File.ReadAllTextAsync(Path.Combine("Baselines", "TestWithMoreThan6Params.generated.txt")).ConfigureAwait(false); + + var (d, r) = await RoslynTestUtils.RunGenerator( + new LoggerMessageGenerator(), + new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly }, + new[] { testSourceCode }).ConfigureAwait(false); + + Assert.Empty(d); + Assert.Single(r); + Assert.Equal(expectedResult, r[0].SourceText.ToString()); + } + + [Fact] + public async Task TestBaseline_TestWithDynamicLogLevel_Success() + { + string testSourceCode = @" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal static partial class TestWithDynamicLogLevel + { + [LoggerMessage(EventId = 9, Message = ""M9"")] + public static partial void M9(LogLevel level, ILogger logger); + } +}"; + string expectedResult = await File.ReadAllTextAsync(Path.Combine("Baselines", "TestWithDynamicLogLevel.generated.txt")).ConfigureAwait(false); + + var (d, r) = await RoslynTestUtils.RunGenerator( + new LoggerMessageGenerator(), + new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly }, + new[] { testSourceCode }).ConfigureAwait(false); + + Assert.Empty(d); + Assert.Single(r); + Assert.Equal(expectedResult, r[0].SourceText.ToString()); + } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 240666e52f745..63e41fdaac5e7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -23,4 +23,16 @@ + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + From 2d41c6dcf603ea977971d9f26868471314576f48 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Apr 2021 10:26:20 -0700 Subject: [PATCH 100/121] - Fix new line issue when comparing baselines --- .../LoggerMessageGeneratorEmitterTests.cs | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index b68036e7e0f7e..c66a267f7c32a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.IO; using System.Reflection; using System.Threading.Tasks; @@ -45,16 +46,7 @@ internal static partial class TestWithOneParam public static partial void M0(ILogger logger, int a1); } }"; - string expectedResult = await File.ReadAllTextAsync(Path.Combine("Baselines", "TestWithOneParam.generated.txt")).ConfigureAwait(false); - - var (d, r) = await RoslynTestUtils.RunGenerator( - new LoggerMessageGenerator(), - new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly }, - new[] { testSourceCode }).ConfigureAwait(false); - - Assert.Empty(d); - Assert.Single(r); - Assert.Equal(expectedResult, r[0].SourceText.ToString()); + await VerifyAgainstBaselineUsingFile("TestWithOneParam.generated.txt", testSourceCode); } [Fact] @@ -70,16 +62,7 @@ internal static partial class TestWithMoreThan6Params public static partial void Method9(ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7); } }"; - string expectedResult = await File.ReadAllTextAsync(Path.Combine("Baselines", "TestWithMoreThan6Params.generated.txt")).ConfigureAwait(false); - - var (d, r) = await RoslynTestUtils.RunGenerator( - new LoggerMessageGenerator(), - new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly }, - new[] { testSourceCode }).ConfigureAwait(false); - - Assert.Empty(d); - Assert.Single(r); - Assert.Equal(expectedResult, r[0].SourceText.ToString()); + await VerifyAgainstBaselineUsingFile("TestWithMoreThan6Params.generated.txt", testSourceCode); } [Fact] @@ -94,7 +77,12 @@ internal static partial class TestWithDynamicLogLevel public static partial void M9(LogLevel level, ILogger logger); } }"; - string expectedResult = await File.ReadAllTextAsync(Path.Combine("Baselines", "TestWithDynamicLogLevel.generated.txt")).ConfigureAwait(false); + await VerifyAgainstBaselineUsingFile("TestWithDynamicLogLevel.generated.txt", testSourceCode); + } + + private async Task VerifyAgainstBaselineUsingFile(string filename, string testSourceCode) + { + string[] expectedLines = await File.ReadAllLinesAsync(Path.Combine("Baselines", filename)).ConfigureAwait(false); var (d, r) = await RoslynTestUtils.RunGenerator( new LoggerMessageGenerator(), @@ -103,7 +91,33 @@ internal static partial class TestWithDynamicLogLevel Assert.Empty(d); Assert.Single(r); - Assert.Equal(expectedResult, r[0].SourceText.ToString()); + + Assert.True(CompareLines(expectedLines, r[0].SourceText, + out string errorMessage), errorMessage); + } + + private bool CompareLines(string[] expectedLines, SourceText sourceText, out string message) + { + if (expectedLines.Length != sourceText.Lines.Count) + { + message = string.Format("Line numbers do not match. Expected: {0} lines, but generated {1}", + expectedLines.Length, sourceText.Lines.Count); + return false; + } + int index = 0; + foreach (TextLine textLine in sourceText.Lines) + { + string expectedLine = expectedLines[index]; + if (!expectedLine.Equals(textLine.ToString(), StringComparison.Ordinal)) + { + message = string.Format("Line {0} does not match.{1}Expected Line:{1}{2}{1}Actual Line:{1}{3}", + textLine.LineNumber, Environment.NewLine, expectedLine, textLine); + return false; + } + index++; + } + message = string.Empty; + return true; } } } From 7e68b1cbcb132ef1909f73c87bc8c286cd8d1896 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Wed, 14 Apr 2021 14:11:34 -0700 Subject: [PATCH 101/121] Add Generator and Resources to the Logging package --- .../gen/Microsoft.Extensions.Logging.Generators.csproj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 255074a0a8c88..5e5523f98ff97 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0 true false true @@ -12,9 +12,16 @@ + + + + + + + From 9200131e99f71013a55146806b5c74aaf5f56452 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Apr 2021 11:02:48 -0700 Subject: [PATCH 102/121] Refactor part 1 --- .../gen/LoggerMessageGenerator.Emitter.cs | 148 +++--------------- 1 file changed, 24 insertions(+), 124 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index f8c57454724ef..b61c52a992b12 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -14,6 +14,7 @@ internal class Emitter { // The maximum arity of LoggerMessage.Define. private const int MaxLoggerMessageDefineArguments = 6; + private const int DefaultStringBuilderCapacity = 1024; private readonly string _generatedCodeAttribute = $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" + @@ -24,23 +25,16 @@ internal class Emitter public string Emit(IReadOnlyList logClasses, CancellationToken cancellationToken) { StringBuilder sb = GetStringBuilder(); - try - { sb.Append("// \n"); sb.Append("#nullable enable\n"); foreach (LoggerClass lc in logClasses) { cancellationToken.ThrowIfCancellationRequested(); - sb.Append(GenType(lc)); + sb.Append(GenType(lc, GetStringBuilder())); } return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } private static string EscapeMessageString(string message) @@ -75,11 +69,8 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm) return result; } - private string GenType(LoggerClass lc) + private string GenType(LoggerClass lc, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { foreach (LoggerMethod lm in lc.Methods) { if (!UseLoggerMessageDefine(lm)) @@ -111,11 +102,6 @@ partial class {lc.Name} {lc.Constraints} }} }} "; - } - finally - { - ReturnStringBuilder(sb); - } } private string GenStruct(LoggerMethod lm) @@ -124,9 +110,9 @@ private string GenStruct(LoggerMethod lm) if (lm.RegularParameters.Count > 0) { constructor = $@" - public __{lm.Name}Struct({GenArguments(lm)}) + public __{lm.Name}Struct({GenArguments(lm, GetStringBuilder())}) {{ -{GenFieldAssignments(lm)} +{GenFieldAssignments(lm, GetStringBuilder())} }} "; } @@ -134,7 +120,7 @@ private string GenStruct(LoggerMethod lm) var toString = $@" public override string ToString() {{ -{GenVariableAssignments(lm)} +{GenVariableAssignments(lm, GetStringBuilder())} return $""{lm.Message}""; }} "; @@ -143,7 +129,7 @@ public override string ToString() [{_generatedCodeAttribute}] private readonly struct __{lm.Name}Struct : global::System.Collections.Generic.IReadOnlyList> {{ -{GenFields(lm)} +{GenFields(lm, GetStringBuilder())} {constructor} {toString} public static string Format(__{lm.Name}Struct state, global::System.Exception? ex) => state.ToString(); @@ -154,7 +140,7 @@ public override string ToString() {{ get => index switch {{ -{GenCases(lm)} +{GenCases(lm, GetStringBuilder())} _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case }}; }} @@ -172,47 +158,28 @@ public override string ToString() "; } - private string GenFields(LoggerMethod lm) + private string GenFields(LoggerMethod lm, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { foreach (LoggerParameter p in lm.RegularParameters) { sb.Append($" private readonly {p.Type} _{p.Name};\n"); } return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } - private string GenFieldAssignments(LoggerMethod lm) + private string GenFieldAssignments(LoggerMethod lm, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { foreach (LoggerParameter p in lm.RegularParameters) { sb.Append($" this._{p.Name} = {p.Name};\n"); } return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } - private string GenVariableAssignments(LoggerMethod lm) + private string GenVariableAssignments(LoggerMethod lm, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { foreach (KeyValuePair t in lm.TemplateMap) { int index = 0; @@ -242,18 +209,10 @@ private string GenVariableAssignments(LoggerMethod lm) } return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } - private string GenCases(LoggerMethod lm) + private string GenCases(LoggerMethod lm, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { int index = 0; foreach (LoggerParameter p in lm.RegularParameters) { @@ -269,36 +228,20 @@ private string GenCases(LoggerMethod lm) sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),\n"); return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } - private string GenCallbackArguments(LoggerMethod lm) + private string GenCallbackArguments(LoggerMethod lm, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { foreach (LoggerParameter p in lm.RegularParameters) { sb.Append($"{p.Name}, "); } return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } - private string GenDefineTypes(LoggerMethod lm, bool brackets) + private string GenDefineTypes(LoggerMethod lm, bool brackets, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { foreach (LoggerParameter p in lm.RegularParameters) { if (sb.Length > 0) @@ -323,18 +266,10 @@ private string GenDefineTypes(LoggerMethod lm, bool brackets) } return result; - } - finally - { - ReturnStringBuilder(sb); - } } - private string GenParameters(LoggerMethod lm) + private string GenParameters(LoggerMethod lm, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { foreach (LoggerParameter p in lm.AllParameters) { if (sb.Length > 0) @@ -346,18 +281,10 @@ private string GenParameters(LoggerMethod lm) } return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } - private string GenArguments(LoggerMethod lm) + private string GenArguments(LoggerMethod lm, StringBuilder sb) { - StringBuilder sb = GetStringBuilder(); - try - { foreach (LoggerParameter p in lm.RegularParameters) { if (sb.Length > 0) @@ -369,20 +296,12 @@ private string GenArguments(LoggerMethod lm) } return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } - private string GenHolder(LoggerMethod lm) + private string GenHolder(LoggerMethod lm, StringBuilder sb) { string typeName = $"__{lm.Name}Struct"; - StringBuilder sb = GetStringBuilder(); - try - { sb.Append($"new {typeName}("); foreach (LoggerParameter p in lm.RegularParameters) { @@ -397,11 +316,6 @@ private string GenHolder(LoggerMethod lm) sb.Append(')'); return sb.ToString(); - } - finally - { - ReturnStringBuilder(sb); - } } private string GenLogMethod(LoggerMethod lm) @@ -470,15 +384,15 @@ private string GenLogMethod(LoggerMethod lm) { return $@" [{_generatedCodeAttribute}] - private static readonly global::System.Action __{lm.Name}Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define{GenDefineTypes(lm, true)}({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message)}""); + private static readonly global::System.Action __{lm.Name}Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define{GenDefineTypes(lm, true, GetStringBuilder())}({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message)}""); [{_generatedCodeAttribute}] - {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm)}) + {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm, GetStringBuilder())}) {{ if ({logger}.IsEnabled({level})) {{ - __{lm.Name}Callback({logger}, {GenCallbackArguments(lm)}{exceptionArg}); + __{lm.Name}Callback({logger}, {GenCallbackArguments(lm, GetStringBuilder())}{exceptionArg}); }} }} "; @@ -487,14 +401,14 @@ private string GenLogMethod(LoggerMethod lm) { return $@" [{_generatedCodeAttribute}] - {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm)}) + {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm, GetStringBuilder())}) {{ if ({logger}.IsEnabled({level})) {{ {logger}.Log( {level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - {GenHolder(lm)}, + {GenHolder(lm, GetStringBuilder())}, {exceptionArg}, __{lm.Name}Struct.Format); }} @@ -568,21 +482,7 @@ private static string __Enumerate(global::System.Collections.IEnumerable? enumer // our own cheezy object pool since we can't use the .NET core version (since this code runs in legacy .NET framework) private StringBuilder GetStringBuilder() { - const int DefaultStringBuilderCapacity = 1024; - - if (_builders.Count == 0) - { - return new StringBuilder(DefaultStringBuilderCapacity); - } - - StringBuilder sb = _builders.Pop(); - sb.Clear(); - return sb; - } - - private void ReturnStringBuilder(StringBuilder sb) - { - _builders.Push(sb); + return new StringBuilder(DefaultStringBuilderCapacity); } } } From 6a71553a78914f9c50ff81bf656b098cbabf8225 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Apr 2021 11:04:06 -0700 Subject: [PATCH 103/121] Fixup whitespaces after removing try/finally --- .../gen/LoggerMessageGenerator.Emitter.cs | 228 +++++++++--------- 1 file changed, 114 insertions(+), 114 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index b61c52a992b12..e7a2cf30eab01 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -20,21 +20,21 @@ internal class Emitter $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" + $"\"{typeof(Emitter).Assembly.GetName().Name}\", " + $"\"{typeof(Emitter).Assembly.GetName().Version}\")"; - private readonly Stack _builders = new (); + private readonly Stack _builders = new(); public string Emit(IReadOnlyList logClasses, CancellationToken cancellationToken) { StringBuilder sb = GetStringBuilder(); - sb.Append("// \n"); - sb.Append("#nullable enable\n"); + sb.Append("// \n"); + sb.Append("#nullable enable\n"); - foreach (LoggerClass lc in logClasses) - { - cancellationToken.ThrowIfCancellationRequested(); - sb.Append(GenType(lc, GetStringBuilder())); - } + foreach (LoggerClass lc in logClasses) + { + cancellationToken.ThrowIfCancellationRequested(); + sb.Append(GenType(lc, GetStringBuilder())); + } - return sb.ToString(); + return sb.ToString(); } private static string EscapeMessageString(string message) @@ -71,29 +71,29 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm) private string GenType(LoggerClass lc, StringBuilder sb) { - foreach (LoggerMethod lm in lc.Methods) + foreach (LoggerMethod lm in lc.Methods) + { + if (!UseLoggerMessageDefine(lm)) { - if (!UseLoggerMessageDefine(lm)) - { - sb.Append(GenStruct(lm)); - } - - sb.Append(GenLogMethod(lm)); + sb.Append(GenStruct(lm)); } - sb.Append(GenEnumerationHelper(lc)); + sb.Append(GenLogMethod(lm)); + } + + sb.Append(GenEnumerationHelper(lc)); - if (string.IsNullOrWhiteSpace(lc.Namespace)) - { - return $@" + if (string.IsNullOrWhiteSpace(lc.Namespace)) + { + return $@" partial class {lc.Name} {lc.Constraints} {{ {sb} }} "; - } + } - return $@" + return $@" namespace {lc.Namespace} {{ partial class {lc.Name} {lc.Constraints} @@ -160,162 +160,162 @@ public override string ToString() private string GenFields(LoggerMethod lm, StringBuilder sb) { - foreach (LoggerParameter p in lm.RegularParameters) - { - sb.Append($" private readonly {p.Type} _{p.Name};\n"); - } + foreach (LoggerParameter p in lm.RegularParameters) + { + sb.Append($" private readonly {p.Type} _{p.Name};\n"); + } - return sb.ToString(); + return sb.ToString(); } private string GenFieldAssignments(LoggerMethod lm, StringBuilder sb) { - foreach (LoggerParameter p in lm.RegularParameters) - { - sb.Append($" this._{p.Name} = {p.Name};\n"); - } + foreach (LoggerParameter p in lm.RegularParameters) + { + sb.Append($" this._{p.Name} = {p.Name};\n"); + } - return sb.ToString(); + return sb.ToString(); } private string GenVariableAssignments(LoggerMethod lm, StringBuilder sb) { - foreach (KeyValuePair t in lm.TemplateMap) + foreach (KeyValuePair t in lm.TemplateMap) + { + int index = 0; + foreach (LoggerParameter p in lm.RegularParameters) { - int index = 0; - foreach (LoggerParameter p in lm.RegularParameters) + if (t.Key.Equals(p.Name, System.StringComparison.OrdinalIgnoreCase)) { - if (t.Key.Equals(p.Name, System.StringComparison.OrdinalIgnoreCase)) - { - break; - } - - index++; + break; } - // check for an index that's too big, this can happen in some cases of malformed input - if (index < lm.RegularParameters.Count) + index++; + } + + // check for an index that's too big, this can happen in some cases of malformed input + if (index < lm.RegularParameters.Count) + { + if (lm.RegularParameters[index].IsEnumerable) { - if (lm.RegularParameters[index].IsEnumerable) - { - sb.Append($" var {t.Key} = " - + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.RegularParameters[index].Name});\n"); - } - else - { - sb.Append($" var {t.Key} = this._{lm.RegularParameters[index].Name};\n"); - } + sb.Append($" var {t.Key} = " + + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.RegularParameters[index].Name});\n"); + } + else + { + sb.Append($" var {t.Key} = this._{lm.RegularParameters[index].Name};\n"); } } + } - return sb.ToString(); + return sb.ToString(); } private string GenCases(LoggerMethod lm, StringBuilder sb) { - int index = 0; - foreach (LoggerParameter p in lm.RegularParameters) + int index = 0; + foreach (LoggerParameter p in lm.RegularParameters) + { + string name = p.Name; + if (lm.TemplateMap.ContainsKey(name)) { - string name = p.Name; - if (lm.TemplateMap.ContainsKey(name)) - { - // take the letter casing from the template - name = lm.TemplateMap[name]; - } - - sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),\n"); + // take the letter casing from the template + name = lm.TemplateMap[name]; } - sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),\n"); - return sb.ToString(); + sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),\n"); + } + + sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),\n"); + return sb.ToString(); } private string GenCallbackArguments(LoggerMethod lm, StringBuilder sb) { - foreach (LoggerParameter p in lm.RegularParameters) - { - sb.Append($"{p.Name}, "); - } + foreach (LoggerParameter p in lm.RegularParameters) + { + sb.Append($"{p.Name}, "); + } - return sb.ToString(); + return sb.ToString(); } private string GenDefineTypes(LoggerMethod lm, bool brackets, StringBuilder sb) { - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) + { + if (sb.Length > 0) { - if (sb.Length > 0) - { - sb.Append(", "); - } - - sb.Append($"{p.Type}"); + sb.Append(", "); } - string result = sb.ToString(); - if (!string.IsNullOrEmpty(result)) + sb.Append($"{p.Type}"); + } + + string result = sb.ToString(); + if (!string.IsNullOrEmpty(result)) + { + if (brackets) { - if (brackets) - { - result = "<" + result + ">"; - } - else - { - result += ", "; - } + result = "<" + result + ">"; + } + else + { + result += ", "; } + } - return result; + return result; } private string GenParameters(LoggerMethod lm, StringBuilder sb) { - foreach (LoggerParameter p in lm.AllParameters) + foreach (LoggerParameter p in lm.AllParameters) + { + if (sb.Length > 0) { - if (sb.Length > 0) - { - sb.Append(", "); - } - - sb.Append($"{p.Type} {p.Name}"); + sb.Append(", "); } - return sb.ToString(); + sb.Append($"{p.Type} {p.Name}"); + } + + return sb.ToString(); } private string GenArguments(LoggerMethod lm, StringBuilder sb) { - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.RegularParameters) + { + if (sb.Length > 0) { - if (sb.Length > 0) - { - sb.Append(", "); - } - - sb.Append($"{p.Type} {p.Name}"); + sb.Append(", "); } - return sb.ToString(); + sb.Append($"{p.Type} {p.Name}"); + } + + return sb.ToString(); } private string GenHolder(LoggerMethod lm, StringBuilder sb) { string typeName = $"__{lm.Name}Struct"; - sb.Append($"new {typeName}("); - foreach (LoggerParameter p in lm.RegularParameters) + sb.Append($"new {typeName}("); + foreach (LoggerParameter p in lm.RegularParameters) + { + if (p != lm.RegularParameters[0]) { - if (p != lm.RegularParameters[0]) - { - sb.Append(", "); - } - - sb.Append(p.Name); + sb.Append(", "); } - sb.Append(')'); + sb.Append(p.Name); + } + + sb.Append(')'); - return sb.ToString(); + return sb.ToString(); } private string GenLogMethod(LoggerMethod lm) From 7547f38fcba8c1d5f86c699731b704cb6037bb84 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Apr 2021 21:13:02 -0700 Subject: [PATCH 104/121] Refactor part 2 --- .../gen/LoggerMessageGenerator.Emitter.cs | 537 +++++++++--------- .../TestWithDynamicLogLevel.generated.txt | 113 ++-- .../TestWithMoreThan6Params.generated.txt | 157 +++-- .../Baselines/TestWithOneParam.generated.txt | 37 +- 4 files changed, 428 insertions(+), 416 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index e7a2cf30eab01..a5e41d9abe40f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -20,21 +20,20 @@ internal class Emitter $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" + $"\"{typeof(Emitter).Assembly.GetName().Name}\", " + $"\"{typeof(Emitter).Assembly.GetName().Version}\")"; - private readonly Stack _builders = new(); + private readonly StringBuilder _builder = new StringBuilder(DefaultStringBuilderCapacity); public string Emit(IReadOnlyList logClasses, CancellationToken cancellationToken) { - StringBuilder sb = GetStringBuilder(); - sb.Append("// \n"); - sb.Append("#nullable enable\n"); + _builder.AppendLine("// "); + _builder.AppendLine("#nullable enable"); foreach (LoggerClass lc in logClasses) { cancellationToken.ThrowIfCancellationRequested(); - sb.Append(GenType(lc, GetStringBuilder())); + GenType(lc); } - return sb.ToString(); + return _builder.ToString(); } private static string EscapeMessageString(string message) @@ -69,116 +68,118 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm) return result; } - private string GenType(LoggerClass lc, StringBuilder sb) + private void GenType(LoggerClass lc) { + if (!string.IsNullOrWhiteSpace(lc.Namespace)) + { + _builder.Append($@" +namespace {lc.Namespace} +{{"); + } + + _builder.Append($@" + partial class {lc.Name} {lc.Constraints} + {{"); + foreach (LoggerMethod lm in lc.Methods) { if (!UseLoggerMessageDefine(lm)) { - sb.Append(GenStruct(lm)); + GenStruct(lm); } - sb.Append(GenLogMethod(lm)); + GenLogMethod(lm); } - sb.Append(GenEnumerationHelper(lc)); + GenEnumerationHelper(lc); - if (string.IsNullOrWhiteSpace(lc.Namespace)) + _builder.Append($@" + }}"); + + if (!string.IsNullOrWhiteSpace(lc.Namespace)) { - return $@" - partial class {lc.Name} {lc.Constraints} - {{ - {sb} - }} - "; + _builder.Append($@" +}}"); } - - return $@" - namespace {lc.Namespace} - {{ - partial class {lc.Name} {lc.Constraints} - {{ - {sb} - }} - }} - "; } - private string GenStruct(LoggerMethod lm) + private void GenStruct(LoggerMethod lm) { - var constructor = string.Empty; + _builder.AppendLine($@" + [{_generatedCodeAttribute}] + private readonly struct __{lm.Name}Struct : global::System.Collections.Generic.IReadOnlyList> + {{"); + GenFields(lm); + if (lm.RegularParameters.Count > 0) { - constructor = $@" - public __{lm.Name}Struct({GenArguments(lm, GetStringBuilder())}) - {{ -{GenFieldAssignments(lm, GetStringBuilder())} - }} -"; + _builder.Append($@" + public __{lm.Name}Struct("); + GenArguments(lm); + _builder.Append($@") + {{"); + _builder.AppendLine(); + GenFieldAssignments(lm); + _builder.Append($@" + }} +"); } - var toString = $@" - public override string ToString() - {{ -{GenVariableAssignments(lm, GetStringBuilder())} - return $""{lm.Message}""; - }} -"; - - return $@" - [{_generatedCodeAttribute}] - private readonly struct __{lm.Name}Struct : global::System.Collections.Generic.IReadOnlyList> - {{ -{GenFields(lm, GetStringBuilder())} -{constructor} -{toString} - public static string Format(__{lm.Name}Struct state, global::System.Exception? ex) => state.ToString(); - - public int Count => {lm.RegularParameters.Count + 1}; - - public global::System.Collections.Generic.KeyValuePair this[int index] - {{ - get => index switch - {{ -{GenCases(lm, GetStringBuilder())} - _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case - }}; - }} - - public global::System.Collections.Generic.IEnumerator> GetEnumerator() - {{ - for (int i = 0; i < {lm.RegularParameters.Count + 1}; i++) - {{ - yield return this[i]; - }} - }} - - global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - }} -"; + _builder.Append($@" + public override string ToString() + {{ +"); + GenVariableAssignments(lm); + _builder.Append($@" + return $""{lm.Message}""; + }} +"); + _builder.Append($@" + public static string Format(__{lm.Name}Struct state, global::System.Exception? ex) => state.ToString(); + + public int Count => {lm.RegularParameters.Count + 1}; + + public global::System.Collections.Generic.KeyValuePair this[int index] + {{ + get => index switch + {{ +"); + GenCases(lm); + _builder.Append($@" + _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case + }}; + }} + + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + {{ + for (int i = 0; i < {lm.RegularParameters.Count + 1}; i++) + {{ + yield return this[i]; + }} + }} + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + }} +"); } - private string GenFields(LoggerMethod lm, StringBuilder sb) + private void GenFields(LoggerMethod lm) { foreach (LoggerParameter p in lm.RegularParameters) { - sb.Append($" private readonly {p.Type} _{p.Name};\n"); + _builder.AppendLine($" private readonly {p.Type} _{p.Name};"); } - - return sb.ToString(); } - private string GenFieldAssignments(LoggerMethod lm, StringBuilder sb) + private void GenFieldAssignments(LoggerMethod lm) { foreach (LoggerParameter p in lm.RegularParameters) { - sb.Append($" this._{p.Name} = {p.Name};\n"); + _builder.AppendLine($" this._{p.Name} = {p.Name};"); } - - return sb.ToString(); } - private string GenVariableAssignments(LoggerMethod lm, StringBuilder sb) + private void GenVariableAssignments(LoggerMethod lm) { foreach (KeyValuePair t in lm.TemplateMap) { @@ -198,20 +199,18 @@ private string GenVariableAssignments(LoggerMethod lm, StringBuilder sb) { if (lm.RegularParameters[index].IsEnumerable) { - sb.Append($" var {t.Key} = " - + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.RegularParameters[index].Name});\n"); + _builder.AppendLine($" var {t.Key} = " + + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.RegularParameters[index].Name});"); } else { - sb.Append($" var {t.Key} = this._{lm.RegularParameters[index].Name};\n"); + _builder.AppendLine($" var {t.Key} = this._{lm.RegularParameters[index].Name};"); } } } - - return sb.ToString(); } - private string GenCases(LoggerMethod lm, StringBuilder sb) + private void GenCases(LoggerMethod lm) { int index = 0; foreach (LoggerParameter p in lm.RegularParameters) @@ -223,201 +222,235 @@ private string GenCases(LoggerMethod lm, StringBuilder sb) name = lm.TemplateMap[name]; } - sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),\n"); + _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),"); } - sb.Append($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),\n"); - return sb.ToString(); + _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),"); } - private string GenCallbackArguments(LoggerMethod lm, StringBuilder sb) + private void GenCallbackArguments(LoggerMethod lm) { foreach (LoggerParameter p in lm.RegularParameters) { - sb.Append($"{p.Name}, "); + _builder.Append($"{p.Name}, "); } - - return sb.ToString(); } - private string GenDefineTypes(LoggerMethod lm, bool brackets, StringBuilder sb) + private void GenDefineTypes(LoggerMethod lm, bool brackets) { - foreach (LoggerParameter p in lm.RegularParameters) + if (lm.RegularParameters.Count == 0) { - if (sb.Length > 0) - { - sb.Append(", "); - } - - sb.Append($"{p.Type}"); + return; + } + if (brackets) + { + _builder.Append("<"); } - string result = sb.ToString(); - if (!string.IsNullOrEmpty(result)) + bool firstItem = true; + foreach (LoggerParameter p in lm.RegularParameters) { - if (brackets) + if (firstItem) { - result = "<" + result + ">"; + firstItem = false; } else { - result += ", "; + _builder.Append(", "); } + + _builder.Append($"{p.Type}"); } - return result; + if (brackets) + { + _builder.Append(">"); + } + else + { + _builder.Append(", "); + } } - private string GenParameters(LoggerMethod lm, StringBuilder sb) + private void GenParameters(LoggerMethod lm) { + bool firstItem = true; foreach (LoggerParameter p in lm.AllParameters) { - if (sb.Length > 0) + if (firstItem) { - sb.Append(", "); + firstItem = false; + } + else + { + _builder.Append(", "); } - sb.Append($"{p.Type} {p.Name}"); + _builder.Append($"{p.Type} {p.Name}"); } - - return sb.ToString(); } - private string GenArguments(LoggerMethod lm, StringBuilder sb) + private void GenArguments(LoggerMethod lm) { + bool firstItem = true; foreach (LoggerParameter p in lm.RegularParameters) { - if (sb.Length > 0) + if (firstItem) { - sb.Append(", "); + firstItem = false; + } + else + { + _builder.Append(", "); } - sb.Append($"{p.Type} {p.Name}"); + _builder.Append($"{p.Type} {p.Name}"); } - - return sb.ToString(); } - private string GenHolder(LoggerMethod lm, StringBuilder sb) + private void GenHolder(LoggerMethod lm) { string typeName = $"__{lm.Name}Struct"; - sb.Append($"new {typeName}("); + _builder.Append($"new {typeName}("); foreach (LoggerParameter p in lm.RegularParameters) { if (p != lm.RegularParameters[0]) { - sb.Append(", "); + _builder.Append(", "); } - sb.Append(p.Name); + _builder.Append(p.Name); } - sb.Append(')'); - - return sb.ToString(); + _builder.Append(')'); } - private string GenLogMethod(LoggerMethod lm) + private void GenLogMethod(LoggerMethod lm) { - string level = string.Empty; + string level = GetLogLevel(lm); + string extension = (lm.IsExtensionMethod ? "this " : string.Empty); + string eventName = string.IsNullOrWhiteSpace(lm.EventName) ? $"nameof({lm.Name})" : $"\"{lm.EventName}\""; + string exceptionArg = GetException(lm); + string logger = GetLogger(lm); - if (lm.Level == null) - { - foreach (LoggerParameter p in lm.AllParameters) - { - if (p.IsLogLevel) - { - level = p.Name; - break; - } - } - } - else + if (UseLoggerMessageDefine(lm)) { - level = lm.Level switch - { - 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", - 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", - 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", - 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", - 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", - 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", - 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", - _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", - }; + _builder.Append($@" + [{_generatedCodeAttribute}] + private static readonly global::System.Action __{lm.Name}Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define"); + + GenDefineTypes(lm, brackets: true); + + _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message)}""); +"); } - string eventName; - if (string.IsNullOrWhiteSpace(lm.EventName)) + _builder.Append($@" + [{_generatedCodeAttribute}] + {lm.Modifiers} void {lm.Name}({extension}"); + + GenParameters(lm); + + _builder.Append($@") + {{ + if ({logger}.IsEnabled({level})) + {{"); + + if (UseLoggerMessageDefine(lm)) { - eventName = $"nameof({lm.Name})"; + _builder.Append($@" + __{lm.Name}Callback({logger}, "); + + GenCallbackArguments(lm); + + _builder.Append(@$"{exceptionArg});"); } else { - eventName = $"\"{lm.EventName}\""; + _builder.Append($@" + {logger}.Log( + {level}, + new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), + "); + GenHolder(lm); + _builder.Append($@", + {exceptionArg}, + __{lm.Name}Struct.Format);"); } - string exceptionArg = "null"; - foreach (LoggerParameter p in lm.AllParameters) + _builder.Append($@" + }} + }}"); + + static string GetException(LoggerMethod lm) { - if (p.IsException) + string exceptionArg = "null"; + foreach (LoggerParameter p in lm.AllParameters) { - exceptionArg = p.Name; - break; + if (p.IsException) + { + exceptionArg = p.Name; + break; + } } + return exceptionArg; } - string logger = lm.LoggerField; - foreach (LoggerParameter p in lm.AllParameters) + static string GetLogger(LoggerMethod lm) { - if (p.IsLogger) + string logger = lm.LoggerField; + foreach (LoggerParameter p in lm.AllParameters) { - logger = p.Name; - break; + if (p.IsLogger) + { + logger = p.Name; + break; + } } + return logger; } - string extension = (lm.IsExtensionMethod ? "this " : string.Empty); - - if (UseLoggerMessageDefine(lm)) - { - return $@" - [{_generatedCodeAttribute}] - private static readonly global::System.Action __{lm.Name}Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define{GenDefineTypes(lm, true, GetStringBuilder())}({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message)}""); - - [{_generatedCodeAttribute}] - {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm, GetStringBuilder())}) - {{ - if ({logger}.IsEnabled({level})) - {{ - __{lm.Name}Callback({logger}, {GenCallbackArguments(lm, GetStringBuilder())}{exceptionArg}); - }} - }} - "; - } - else + static string GetLogLevel(LoggerMethod lm) { - return $@" - [{_generatedCodeAttribute}] - {lm.Modifiers} void {lm.Name}({extension}{GenParameters(lm, GetStringBuilder())}) - {{ - if ({logger}.IsEnabled({level})) - {{ - {logger}.Log( - {level}, - new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), - {GenHolder(lm, GetStringBuilder())}, - {exceptionArg}, - __{lm.Name}Struct.Format); - }} - }} - "; + string level = string.Empty; + + if (lm.Level == null) + { + foreach (LoggerParameter p in lm.AllParameters) + { + if (p.IsLogLevel) + { + level = p.Name; + break; + } + } + } + else + { + level = lm.Level switch + { + 0 => "global::Microsoft.Extensions.Logging.LogLevel.Trace", + 1 => "global::Microsoft.Extensions.Logging.LogLevel.Debug", + 2 => "global::Microsoft.Extensions.Logging.LogLevel.Information", + 3 => "global::Microsoft.Extensions.Logging.LogLevel.Warning", + 4 => "global::Microsoft.Extensions.Logging.LogLevel.Error", + 5 => "global::Microsoft.Extensions.Logging.LogLevel.Critical", + 6 => "global::Microsoft.Extensions.Logging.LogLevel.None", + _ => $"(global::Microsoft.Extensions.Logging.LogLevel){lm.Level}", + }; + } + + return level; } } - private string GenEnumerationHelper(LoggerClass lc) + private void GenEnumerationHelper(LoggerClass lc) { foreach (LoggerMethod lm in lc.Methods) { @@ -427,62 +460,54 @@ private string GenEnumerationHelper(LoggerClass lc) { if (p.IsEnumerable) { - return $@" - [{_generatedCodeAttribute}] - private static string __Enumerate(global::System.Collections.IEnumerable? enumerable) - {{ - if (enumerable == null) - {{ - return ""(null)""; - }} - - var sb = new global::System.Text.StringBuilder(); - _ = sb.Append('['); - - bool first = true; - foreach (object e in enumerable) - {{ - if (!first) - {{ - _ = sb.Append("", ""); - }} - - if (e == null) - {{ - _ = sb.Append(""(null)""); - }} - else - {{ - if (e is global::System.IFormattable fmt) - {{ - _ = sb.Append(fmt.ToString(null, global::System.Globalization.CultureInfo.InvariantCulture)); - }} - else - {{ - _ = sb.Append(e); - }} - }} - - first = false; - }} - - _ = sb.Append(']'); - - return sb.ToString(); - }} -"; + _builder.Append($@" + [{_generatedCodeAttribute}] + private static string __Enumerate(global::System.Collections.IEnumerable? enumerable) + {{ + if (enumerable == null) + {{ + return ""(null)""; + }} + + var sb = new global::System.Text.StringBuilder(); + _ = sb.Append('['); + + bool first = true; + foreach (object e in enumerable) + {{ + if (!first) + {{ + _ = sb.Append("", ""); + }} + + if (e == null) + {{ + _ = sb.Append(""(null)""); + }} + else + {{ + if (e is global::System.IFormattable fmt) + {{ + _ = sb.Append(fmt.ToString(null, global::System.Globalization.CultureInfo.InvariantCulture)); + }} + else + {{ + _ = sb.Append(e); + }} + }} + + first = false; + }} + + _ = sb.Append(']'); + + return sb.ToString(); + }} +"); } } } } - - return string.Empty; - } - - // our own cheezy object pool since we can't use the .NET core version (since this code runs in legacy .NET framework) - private StringBuilder GetStringBuilder() - { - return new StringBuilder(DefaultStringBuilderCapacity); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt index 96a3ca78a9e69..a5f70acb7c6b3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithDynamicLogLevel.generated.txt @@ -1,62 +1,57 @@ // #nullable enable - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses - { - partial class TestWithDynamicLogLevel - { - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - private readonly struct __M9Struct : global::System.Collections.Generic.IReadOnlyList> - { - - - - public override string ToString() - { - - return $"M9"; - } - - public static string Format(__M9Struct state, global::System.Exception? ex) => state.ToString(); - - public int Count => 1; - - public global::System.Collections.Generic.KeyValuePair this[int index] - { - get => index switch - { - 0 => new global::System.Collections.Generic.KeyValuePair("{OriginalFormat}", "M9"), - - _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case - }; - } - - public global::System.Collections.Generic.IEnumerator> GetEnumerator() - { - for (int i = 0; i < 1; i++) - { - yield return this[i]; - } - } - - global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - public static partial void M9(Microsoft.Extensions.Logging.LogLevel level, Microsoft.Extensions.Logging.ILogger logger) - { - if (logger.IsEnabled(level)) - { - logger.Log( - level, - new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)), - new __M9Struct(), - null, - __M9Struct.Format); - } - } - - } - } - \ No newline at end of file +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithDynamicLogLevel + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private readonly struct __M9Struct : global::System.Collections.Generic.IReadOnlyList> + { + + public override string ToString() + { + + return $"M9"; + } + + public static string Format(__M9Struct state, global::System.Exception? ex) => state.ToString(); + + public int Count => 1; + + public global::System.Collections.Generic.KeyValuePair this[int index] + { + get => index switch + { + 0 => new global::System.Collections.Generic.KeyValuePair("{OriginalFormat}", "M9"), + + _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case + }; + } + + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + { + for (int i = 0; i < 1; i++) + { + yield return this[i]; + } + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void M9(Microsoft.Extensions.Logging.LogLevel level, Microsoft.Extensions.Logging.ILogger logger) + { + if (logger.IsEnabled(level)) + { + logger.Log( + level, + new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)), + new __M9Struct(), + null, + __M9Struct.Format); + } + } + } +} \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt index 37d44ae2fe704..d2cfaaf135284 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt @@ -1,95 +1,90 @@ // #nullable enable - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses - { - partial class TestWithMoreThan6Params - { - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - private readonly struct __Method9Struct : global::System.Collections.Generic.IReadOnlyList> - { - private readonly int _p1; - private readonly int _p2; - private readonly int _p3; - private readonly int _p4; - private readonly int _p5; - private readonly int _p6; - private readonly int _p7; +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithMoreThan6Params + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private readonly struct __Method9Struct : global::System.Collections.Generic.IReadOnlyList> + { + private readonly int _p1; + private readonly int _p2; + private readonly int _p3; + private readonly int _p4; + private readonly int _p5; + private readonly int _p6; + private readonly int _p7; + public __Method9Struct(int p1, int p2, int p3, int p4, int p5, int p6, int p7) + { + this._p1 = p1; + this._p2 = p2; + this._p3 = p3; + this._p4 = p4; + this._p5 = p5; + this._p6 = p6; + this._p7 = p7; - public __Method9Struct(int p1, int p2, int p3, int p4, int p5, int p6, int p7) - { - this._p1 = p1; - this._p2 = p2; - this._p3 = p3; - this._p4 = p4; - this._p5 = p5; - this._p6 = p6; - this._p7 = p7; + } - } + public override string ToString() + { + var p1 = this._p1; + var p2 = this._p2; + var p3 = this._p3; + var p4 = this._p4; + var p5 = this._p5; + var p6 = this._p6; + var p7 = this._p7; + return $"M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"; + } - public override string ToString() - { - var p1 = this._p1; - var p2 = this._p2; - var p3 = this._p3; - var p4 = this._p4; - var p5 = this._p5; - var p6 = this._p6; - var p7 = this._p7; + public static string Format(__Method9Struct state, global::System.Exception? ex) => state.ToString(); - return $"M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"; - } + public int Count => 8; - public static string Format(__Method9Struct state, global::System.Exception? ex) => state.ToString(); + public global::System.Collections.Generic.KeyValuePair this[int index] + { + get => index switch + { + 0 => new global::System.Collections.Generic.KeyValuePair("p1", this._p1), + 1 => new global::System.Collections.Generic.KeyValuePair("p2", this._p2), + 2 => new global::System.Collections.Generic.KeyValuePair("p3", this._p3), + 3 => new global::System.Collections.Generic.KeyValuePair("p4", this._p4), + 4 => new global::System.Collections.Generic.KeyValuePair("p5", this._p5), + 5 => new global::System.Collections.Generic.KeyValuePair("p6", this._p6), + 6 => new global::System.Collections.Generic.KeyValuePair("p7", this._p7), + 7 => new global::System.Collections.Generic.KeyValuePair("{OriginalFormat}", "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"), - public int Count => 8; + _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case + }; + } - public global::System.Collections.Generic.KeyValuePair this[int index] - { - get => index switch - { - 0 => new global::System.Collections.Generic.KeyValuePair("p1", this._p1), - 1 => new global::System.Collections.Generic.KeyValuePair("p2", this._p2), - 2 => new global::System.Collections.Generic.KeyValuePair("p3", this._p3), - 3 => new global::System.Collections.Generic.KeyValuePair("p4", this._p4), - 4 => new global::System.Collections.Generic.KeyValuePair("p5", this._p5), - 5 => new global::System.Collections.Generic.KeyValuePair("p6", this._p6), - 6 => new global::System.Collections.Generic.KeyValuePair("p7", this._p7), - 7 => new global::System.Collections.Generic.KeyValuePair("{OriginalFormat}", "M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"), + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + { + for (int i = 0; i < 8; i++) + { + yield return this[i]; + } + } - _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case - }; - } + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } - public global::System.Collections.Generic.IEnumerator> GetEnumerator() - { - for (int i = 0; i < 8; i++) - { - yield return this[i]; - } - } - - global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - public static partial void Method9(Microsoft.Extensions.Logging.ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7) - { - if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error)) - { - logger.Log( - global::Microsoft.Extensions.Logging.LogLevel.Error, - new global::Microsoft.Extensions.Logging.EventId(8, nameof(Method9)), - new __Method9Struct(p1, p2, p3, p4, p5, p6, p7), - null, - __Method9Struct.Format); - } - } - - } - } - \ No newline at end of file + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void Method9(Microsoft.Extensions.Logging.ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7) + { + if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error)) + { + logger.Log( + global::Microsoft.Extensions.Logging.LogLevel.Error, + new global::Microsoft.Extensions.Logging.EventId(8, nameof(Method9)), + new __Method9Struct(p1, p2, p3, p4, p5, p6, p7), + null, + __Method9Struct.Format); + } + } + } +} \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt index d4cce82b3660d..01824e4f52286 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt @@ -1,24 +1,21 @@ // #nullable enable - namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses - { - partial class TestWithOneParam - { - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - private static readonly global::System.Action __M0Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {A1}"); +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithOneParam + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + private static readonly global::System.Action __M0Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {A1}"); - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - public static partial void M0(Microsoft.Extensions.Logging.ILogger logger, int a1) - { - if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error)) - { - __M0Callback(logger, a1, null); - } - } - - } - } - \ No newline at end of file + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] + public static partial void M0(Microsoft.Extensions.Logging.ILogger logger, int a1) + { + if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error)) + { + __M0Callback(logger, a1, null); + } + } + } +} \ No newline at end of file From 6eab93d4dd3ac39756fb52f38cdd39742e8478b0 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Apr 2021 21:43:20 -0700 Subject: [PATCH 105/121] - Remove duplication in the resource string values - Fix random PR feedback --- .../gen/DiagnosticDescriptors.cs | 26 ++++---- .../gen/LoggerMessageGenerator.Parser.cs | 2 +- ...osoft.Extensions.Logging.Generators.csproj | 1 - .../gen/Resources/Strings.resx | 38 +----------- .../gen/Resources/xlf/Strings.cs.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.de.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.es.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.fr.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.it.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.ja.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.ko.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.pl.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.pt-BR.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.ru.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.tr.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.zh-Hans.xlf | 62 +------------------ .../gen/Resources/xlf/Strings.zh-Hant.xlf | 62 +------------------ ...Extensions.Logging.Generators.Tests.csproj | 3 +- 18 files changed, 30 insertions(+), 846 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs index 84ecdd81ca822..c196d6f93648c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs @@ -10,7 +10,7 @@ public static class DiagnosticDescriptors { public static DiagnosticDescriptor InvalidLoggingMethodName { get; } = new ( id: "SYSLIB1001", - title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -19,14 +19,14 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor ShouldntMentionLogLevelInMessage { get; } = new ( id: "SYSLIB1002", title: new LocalizableResourceString(nameof(SR.ShouldntMentionLogLevelInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), - messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionLogLevelInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static DiagnosticDescriptor InvalidLoggingMethodParameterName { get; } = new ( id: "SYSLIB1003", - title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -34,7 +34,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor LoggingMethodInNestedType { get; } = new ( id: "SYSLIB1004", - title: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -58,7 +58,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor LoggingMethodMustReturnVoid { get; } = new ( id: "SYSLIB1007", - title: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -66,7 +66,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor MissingLoggerArgument { get; } = new ( id: "SYSLIB1008", - title: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -74,7 +74,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor LoggingMethodShouldBeStatic { get; } = new ( id: "SYSLIB1009", - title: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Warning, @@ -82,7 +82,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor LoggingMethodMustBePartial { get; } = new ( id: "SYSLIB1010", - title: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -90,7 +90,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor LoggingMethodIsGeneric { get; } = new ( id: "SYSLIB1011", - title: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -107,7 +107,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor ShouldntMentionExceptionInMessage { get; } = new ( id: "SYSLIB1013", title: new LocalizableResourceString(nameof(SR.ShouldntMentionExceptionInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), - messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionExceptionInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Warning, isEnabledByDefault: true); @@ -130,7 +130,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor LoggingMethodHasBody { get; } = new ( id: "SYSLIB1016", - title: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -138,7 +138,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor MissingLogLevel { get; } = new ( id: "SYSLIB1017", - title: new LocalizableResourceString(nameof(SR.MissingLogLevelTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + title: new LocalizableResourceString(nameof(SR.MissingLogLevelMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingLogLevelMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Error, @@ -147,7 +147,7 @@ public static class DiagnosticDescriptors public static DiagnosticDescriptor ShouldntMentionLoggerInMessage { get; } = new ( id: "SYSLIB1018", title: new LocalizableResourceString(nameof(SR.ShouldntMentionLoggerInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), - messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionLoggerInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), category: "LoggingGenerator", DiagnosticSeverity.Warning, isEnabledByDefault: true); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index a1bf8b8ea9d46..2ff276b3f2d5d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -34,7 +34,7 @@ public IReadOnlyList GetLogClasses(IEnumerable(); diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj index 5e5523f98ff97..122f8bd4bec4e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Microsoft.Extensions.Logging.Generators.csproj @@ -20,7 +20,6 @@ - diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx index d5d5aa0803368..e362c271a1d7c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx @@ -117,21 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging class cannot be in nested types - Logging class cannot be in nested types @@ -147,37 +138,22 @@ Multiple logging methods are using event id {0} in class {1} - - Logging methods must return void - Logging methods must return void - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Logging methods must be static - Logging methods must be static - - Logging methods must be partial - Logging methods must be partial - - Logging methods cannot be generic - Logging methods cannot be generic - + Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -201,27 +177,15 @@ Logging template has no corresponding method argument - - Logging methods cannot have a body - Logging methods cannot have a body - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf index a7c3416acc025..6e43f90b3695d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf index 997e908e41542..0b9dc1668218f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf index d3d352cb0e96e..7f6ea1ec1d8ab 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf index de09348f11599..3173efa5141c8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf index 3810e9e76c10e..b1491256ed4af 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf index 63519eff7a8bb..e1dd3ede61222 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf index 7ca8ad5f5f525..a5ce7d98e2a90 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf index d52e3de1df7f7..ffc6e780c4a05 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf index 7a0abe022a384..de2c3f2383319 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf index 3d5aa62a6b1c1..602e401392b18 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf index df3366affc9b4..1cfde5ccd82d6 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf index 28b8e3280ec95..66eee410587bb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf index bde016d6921dc..47338492fde0b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -17,101 +17,51 @@ Logging method names cannot start with _ - - Logging method names cannot start with _ - Logging method names cannot start with _ - - Logging method parameter names cannot start with _ Logging method parameter names cannot start with _ - - Logging method parameter names cannot start with _ - Logging method parameter names cannot start with _ - - Logging methods cannot have a body Logging methods cannot have a body - - Logging methods cannot have a body - Logging methods cannot have a body - - Logging class cannot be in nested types Logging class cannot be in nested types - - Logging class cannot be in nested types - Logging class cannot be in nested types - - Logging methods cannot be generic Logging methods cannot be generic - - Logging methods cannot be generic - Logging methods cannot be generic - - Logging methods must be partial Logging methods must be partial - - Logging methods must be partial - Logging methods must be partial - - Logging methods must return void Logging methods must return void - - Logging methods must return void - Logging methods must return void - - Logging methods must be static Logging methods must be static - - Logging methods must be static - Logging methods must be static - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - - Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} @@ -152,17 +102,12 @@ Redundant qualifier in logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include exception parameters as templates in the logging message Don't include exception parameters as templates in the logging message - + Don't include a template for {0} in the logging message since it is implicitly taken care of Don't include a template for {0} in the logging message since it is implicitly taken care of @@ -172,11 +117,6 @@ Don't include log level parameters as templates in the logging message - - Don't include a template for {0} in the logging message since it is implicitly taken care of - Don't include a template for {0} in the logging message since it is implicitly taken care of - - Don't include logger parameters as templates in the logging message Don't include logger parameters as templates in the logging message diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 63e41fdaac5e7..efefb4b8a8070 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -13,7 +13,8 @@ - + + From cc0bb07586d7faf1652202678e39cbd5db5aa769 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Apr 2021 22:02:20 -0700 Subject: [PATCH 106/121] Replace operations using StringComparison.Ordinal --- .../gen/LoggerMessageGenerator.Emitter.cs | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index a5e41d9abe40f..88753ba56598c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -39,9 +39,9 @@ public string Emit(IReadOnlyList logClasses, CancellationToken canc private static string EscapeMessageString(string message) { return message - .Replace("\n", "\\n") - .Replace("\r", "\\r") - .Replace("\"", "\\\""); + .ReplaceOrdinal("\n", "\\n") + .ReplaceOrdinal("\r", "\\r") + .ReplaceOrdinal("\"", "\\\""); } private static bool UseLoggerMessageDefine(LoggerMethod lm) @@ -511,4 +511,38 @@ private static string __Enumerate(global::System.Collections.IEnumerable? enumer } } } + + internal static class StringHelper + { + // Replace operations need to be done with StringComparison.Ordinal + // (Adding code snippet below, since the API taking StringComparison is not available in netstandard2.0) + internal static string ReplaceOrdinal(this string s, string oldValue, string newValue) + { + int index = s.IndexOf(oldValue, StringComparison.Ordinal); + if (index < 0) + { + return s; + } + + StringBuilder sb = new StringBuilder(s.Length); + sb.Append(s, 0, index); + sb.Append(newValue); + index += oldValue.Length; + int newIndex; + + while (index < s.Length && (newIndex = s.IndexOf(oldValue, index, StringComparison.Ordinal)) > 0) + { + sb.Append(s, index, newIndex - index); + sb.Append(newValue); + index = newIndex + oldValue.Length; + } + + if (index < s.Length) + { + sb.Append(s, index, s.Length - index); + } + + return sb.ToString(); + } + } } From e56d447106e5b92faa62b685851610e0e0f351ba Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Apr 2021 22:25:23 -0700 Subject: [PATCH 107/121] Include generator project to src.proj --- src/libraries/src.proj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/src.proj b/src/libraries/src.proj index 2a2f28c8567d0..7cfb0f65e3d0c 100644 --- a/src/libraries/src.proj +++ b/src/libraries/src.proj @@ -11,6 +11,7 @@ Exclude="@(NetCoreAppLibrary->'%(Identity)\src\%(Identity).csproj')" /> From b970fdd49228229249d9268cb65dfa27a7c4839c Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Wed, 14 Apr 2021 23:09:33 -0700 Subject: [PATCH 108/121] Upgrade generator package versions --- eng/Versions.props | 4 ++-- .../Microsoft.Extensions.Logging.Generators.Tests.csproj | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index c13010725ce84..c6584bcb808e4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,8 +46,8 @@ - 3.8.0 - 3.8.0 + 3.10.0-1.final + 3.10.0-1.final 6.0.0-preview3.21168.1 diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index efefb4b8a8070..d37d790d0777d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent) true true + 3.10.0-1.final From bd93ccfe697187344a9971e6bac7a0212ea0c23d Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 07:58:32 -0700 Subject: [PATCH 109/121] - Can't have a log level set twice set in both the attribute and as a logging method parameter --- .../gen/LoggerMessageGenerator.Parser.cs | 6 ++- .../LoggerMessageGeneratorParserTests.cs | 40 ++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 2ff276b3f2d5d..52a280d259912 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -206,7 +206,7 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable diagnostics = await RunGenerator(@" partial class C { [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] - static partial void M1(ILogger logger, LogLevel level); + static partial void M1(ILogger logger, LogLevel levelParam); } "); Assert.Single(diagnostics); - Assert.Equal(DiagnosticDescriptors.XXX.Id, diagnostics[0].Id); + Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id); } + [Fact] + public async Task LogLevelDoublySet_AndInMessageTemplate_ProducesDiagnostic() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {level2}"")] + static partial void M1(ILogger logger, LogLevel level1, LogLevel level2); + } + "); + + Assert.Single(diagnostics); + Assert.Equal(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate.Id, diagnostics[0].Id); + } + + [Fact] + public async Task DoubleLogLevel_FirstOneSetAsMethodParameter_SecondOneInMessageTemplate_Supported() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C + { + [LoggerMessage(EventId = 0, Message = ""M1 {level2}"")] + static partial void M1(ILogger logger, LogLevel level1, LogLevel level2); + } + "); + + Assert.Empty(diagnostics); + } + +#if false // TODO: can't have the same template with different casing [Fact] public async Task InconsistentTemplateCasing() @@ -613,7 +641,7 @@ private static async Task> RunGenerator( Assembly[]? refs = null; if (includeLoggingReferences) { - refs = new[] { Assembly.GetAssembly(typeof(ILogger))!, Assembly.GetAssembly(typeof(LoggerMessageAttribute))! }; + refs = new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly }; } var (d, r) = await RoslynTestUtils.RunGenerator( From a6ecab33905a02f46b82ee1f5e85f458634676dc Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 08:08:23 -0700 Subject: [PATCH 110/121] Add three more diagnostics --- docs/project/list-of-diagnostics.md | 5 +++- .../gen/DiagnosticDescriptors.cs | 24 +++++++++++++++++++ .../gen/Resources/Strings.resx | 9 +++++++ .../gen/Resources/xlf/Strings.cs.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.de.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.es.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.fr.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.it.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.ja.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.ko.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.pl.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.pt-BR.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.ru.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.tr.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.zh-Hans.xlf | 15 ++++++++++++ .../gen/Resources/xlf/Strings.zh-Hant.xlf | 15 ++++++++++++ .../LoggerMessageGeneratorParserTests.cs | 4 ++-- 17 files changed, 234 insertions(+), 3 deletions(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index feb0568ea6097..4e0069152591c 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -50,4 +50,7 @@ Currently the identifiers `SYSLIB0001` through `SYSLIB0999` are carved out for o | __`SYSLIB1017`__ | A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method | | __`SYSLIB1018`__ | Don't include logger parameters as templates in the logging message | | __`SYSLIB1019`__ | Couldn't find a field of type Microsoft.Extensions.Logging.ILogger | -| __`SYSLIB1020`__ | Found multiple fields of type Microsoft.Extensions.Logging.ILogger | \ No newline at end of file +| __`SYSLIB1020`__ | Found multiple fields of type Microsoft.Extensions.Logging.ILogger | +| __`SYSLIB1021`__ | Can't have the same template with different casing | +| __`SYSLIB1022`__ | Can't have malformed format strings (like dangling {, etc) | +| __`SYSLIB1023`__ | Generating more than 6 arguments is not supported | \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs index c196d6f93648c..3d8bc17722d49 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs @@ -167,5 +167,29 @@ public static class DiagnosticDescriptors category: "LoggingGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static DiagnosticDescriptor InconsistentTemplateCasing { get; } = new( + id: "SYSLIB1021", + title: new LocalizableResourceString(nameof(SR.InconsistentTemplateCasingMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.InconsistentTemplateCasingMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor MalformedFormatStrings { get; } = new( + id: "SYSLIB1022", + title: new LocalizableResourceString(nameof(SR.MalformedFormatStringsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.MalformedFormatStringsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor GeneratingForMax6Arguments { get; } = new( + id: "SYSLIB1023", + title: new LocalizableResourceString(nameof(SR.GeneratingForMax6ArgumentsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + messageFormat: new LocalizableResourceString(nameof(SR.GeneratingForMax6ArgumentsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), + category: "LoggingGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx index e362c271a1d7c..e00ea13e8aa01 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx @@ -201,4 +201,13 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger + + Can't have the same template with different casing + + + Can't have malformed format strings (like dangling {, etc) + + + Generating more than 6 arguments is not supported + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf index 6e43f90b3695d..43553ee95be5f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf index 0b9dc1668218f..d29712e845e01 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf index 7f6ea1ec1d8ab..8a9dc158a7097 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf index 3173efa5141c8..436d7d00b4d05 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf index b1491256ed4af..dbb0ebc47518c 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf index e1dd3ede61222..38065be7c9537 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf index a5ce7d98e2a90..1adc3f3f1d839 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf index ffc6e780c4a05..66acfb696c33b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf index de2c3f2383319..e4e305783e844 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf index 602e401392b18..215ce1eb850c3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf index 1cfde5ccd82d6..167819d1e1e04 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf index 66eee410587bb..5bb70e2e58d86 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf index 47338492fde0b..6677de9ff9ac7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -12,6 +12,16 @@ Argument is not referenced from the logging message + + Generating more than 6 arguments is not supported + Generating more than 6 arguments is not supported + + + + Can't have the same template with different casing + Can't have the same template with different casing + + Logging method names cannot start with _ Logging method names cannot start with _ @@ -52,6 +62,11 @@ Logging methods must be static + + Can't have malformed format strings (like dangling {, etc) + Can't have malformed format strings (like dangling {, etc) + + A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method A LogLevel value must be supplied in the LoggerMessage attribute or as a parameter to the logging method diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 04ce74553d057..1aabf53997b2a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -212,7 +212,7 @@ partial class C "); Assert.Single(diagnostics); - Assert.Equal(DiagnosticDescriptors.XXX.Id, diagnostics[0].Id); + Assert.Equal(DiagnosticDescriptors.InconsistentTemplateCasing.Id, diagnostics[0].Id); } // TODO: can't have malformed format strings (like dangling {, etc) @@ -228,7 +228,7 @@ partial class C "); Assert.Single(diagnostics); - Assert.Equal(DiagnosticDescriptors.XXX.Id, diagnostics[0].Id); + Assert.Equal(DiagnosticDescriptors.MalformedFormatStrings.Id, diagnostics[0].Id); } #endif From 35a584c530ea9c6cb7668694783ef7fb2d3b5ce6 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 09:01:10 -0700 Subject: [PATCH 111/121] Use wildcard, include gen projects under NonNetCoreApp --- src/libraries/src.proj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/src.proj b/src/libraries/src.proj index 7cfb0f65e3d0c..c0738c440374f 100644 --- a/src/libraries/src.proj +++ b/src/libraries/src.proj @@ -9,9 +9,10 @@ Exclude="@(ProjectExclusions)" /> + From 0ec16c44621814defc413434a268b534421c1790 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 09:22:11 -0700 Subject: [PATCH 112/121] Renames only --- .../gen/LoggerMessageGenerator.Emitter.cs | 44 +++++++++---------- .../gen/LoggerMessageGenerator.Parser.cs | 10 ++--- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index 88753ba56598c..c329a43ed89b5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -47,9 +47,9 @@ private static string EscapeMessageString(string message) private static bool UseLoggerMessageDefine(LoggerMethod lm) { bool result = - (lm.RegularParameters.Count <= MaxLoggerMessageDefineArguments) && // more args than LoggerMessage.Define can handle - (lm.Level != null) && // dynamic log level, which LoggerMessage.Define can't handle - (lm.TemplateList.Count == lm.RegularParameters.Count); // mismatch in template to args, which LoggerMessage.Define can't handle + (lm.TemplateParameters.Count <= MaxLoggerMessageDefineArguments) && // more args than LoggerMessage.Define can handle + (lm.Level != null) && // dynamic log level, which LoggerMessage.Define can't handle + (lm.TemplateList.Count == lm.TemplateParameters.Count); // mismatch in template to args, which LoggerMessage.Define can't handle if (result) { @@ -57,7 +57,7 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm) int count = 0; foreach (string t in lm.TemplateList) { - if (!t.Equals(lm.RegularParameters[count].Name, StringComparison.OrdinalIgnoreCase)) + if (!t.Equals(lm.TemplateParameters[count].Name, StringComparison.OrdinalIgnoreCase)) { // order doesn't match, can't use LoggerMessage.Define return false; @@ -111,7 +111,7 @@ private void GenStruct(LoggerMethod lm) {{"); GenFields(lm); - if (lm.RegularParameters.Count > 0) + if (lm.TemplateParameters.Count > 0) { _builder.Append($@" public __{lm.Name}Struct("); @@ -137,7 +137,7 @@ public override string ToString() _builder.Append($@" public static string Format(__{lm.Name}Struct state, global::System.Exception? ex) => state.ToString(); - public int Count => {lm.RegularParameters.Count + 1}; + public int Count => {lm.TemplateParameters.Count + 1}; public global::System.Collections.Generic.KeyValuePair this[int index] {{ @@ -152,7 +152,7 @@ public override string ToString() public global::System.Collections.Generic.IEnumerator> GetEnumerator() {{ - for (int i = 0; i < {lm.RegularParameters.Count + 1}; i++) + for (int i = 0; i < {lm.TemplateParameters.Count + 1}; i++) {{ yield return this[i]; }} @@ -165,7 +165,7 @@ public override string ToString() private void GenFields(LoggerMethod lm) { - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { _builder.AppendLine($" private readonly {p.Type} _{p.Name};"); } @@ -173,7 +173,7 @@ private void GenFields(LoggerMethod lm) private void GenFieldAssignments(LoggerMethod lm) { - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { _builder.AppendLine($" this._{p.Name} = {p.Name};"); } @@ -184,7 +184,7 @@ private void GenVariableAssignments(LoggerMethod lm) foreach (KeyValuePair t in lm.TemplateMap) { int index = 0; - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { if (t.Key.Equals(p.Name, System.StringComparison.OrdinalIgnoreCase)) { @@ -195,16 +195,16 @@ private void GenVariableAssignments(LoggerMethod lm) } // check for an index that's too big, this can happen in some cases of malformed input - if (index < lm.RegularParameters.Count) + if (index < lm.TemplateParameters.Count) { - if (lm.RegularParameters[index].IsEnumerable) + if (lm.TemplateParameters[index].IsEnumerable) { _builder.AppendLine($" var {t.Key} = " - + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.RegularParameters[index].Name});"); + + $"__Enumerate((global::System.Collections.IEnumerable ?)this._{lm.TemplateParameters[index].Name});"); } else { - _builder.AppendLine($" var {t.Key} = this._{lm.RegularParameters[index].Name};"); + _builder.AppendLine($" var {t.Key} = this._{lm.TemplateParameters[index].Name};"); } } } @@ -213,7 +213,7 @@ private void GenVariableAssignments(LoggerMethod lm) private void GenCases(LoggerMethod lm) { int index = 0; - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { string name = p.Name; if (lm.TemplateMap.ContainsKey(name)) @@ -230,7 +230,7 @@ private void GenCases(LoggerMethod lm) private void GenCallbackArguments(LoggerMethod lm) { - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { _builder.Append($"{p.Name}, "); } @@ -238,7 +238,7 @@ private void GenCallbackArguments(LoggerMethod lm) private void GenDefineTypes(LoggerMethod lm, bool brackets) { - if (lm.RegularParameters.Count == 0) + if (lm.TemplateParameters.Count == 0) { return; } @@ -248,7 +248,7 @@ private void GenDefineTypes(LoggerMethod lm, bool brackets) } bool firstItem = true; - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { if (firstItem) { @@ -293,7 +293,7 @@ private void GenParameters(LoggerMethod lm) private void GenArguments(LoggerMethod lm) { bool firstItem = true; - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { if (firstItem) { @@ -313,9 +313,9 @@ private void GenHolder(LoggerMethod lm) string typeName = $"__{lm.Name}Struct"; _builder.Append($"new {typeName}("); - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { - if (p != lm.RegularParameters[0]) + if (p != lm.TemplateParameters[0]) { _builder.Append(", "); } @@ -456,7 +456,7 @@ private void GenEnumerationHelper(LoggerClass lc) { if (UseLoggerMessageDefine(lm)) { - foreach (LoggerParameter p in lm.RegularParameters) + foreach (LoggerParameter p in lm.TemplateParameters) { if (p.IsEnumerable) { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 52a280d259912..4dccac129c311 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -259,7 +259,7 @@ public IReadOnlyList GetLogClasses(IEnumerable GetLogClasses(IEnumerable AllParameters = new (); - public readonly List RegularParameters = new (); + public readonly List TemplateParameters = new (); public readonly Dictionary TemplateMap = new (StringComparer.OrdinalIgnoreCase); public readonly List TemplateList = new (); public string Name = string.Empty; @@ -653,7 +653,7 @@ internal class LoggerParameter public bool IsException; public bool IsLogLevel; public bool IsEnumerable; - public bool IsRegular => !IsLogger && !IsException && !IsLogLevel; + public bool IsTemplateParameter => !IsLogger && !IsException && !IsLogLevel; } } } From e81015d409c8e05afe170507ed09b1afbba089c3 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 09:26:36 -0700 Subject: [PATCH 113/121] - Use other Define overload using skipEnabledCheck - .Clear() the builder in between calls to Emit - Add comments - Use globbing pattern for *.generated.txt - Optimize code to convert message to escape form - Move example/remarks in triple slash docs for attribute class - Don't call context.AddSource when no logClass are there --- .../gen/LoggerMessageGenerator.Emitter.cs | 57 ++++++++++++++++--- .../gen/LoggerMessageGenerator.Parser.cs | 2 + .../gen/LoggerMessageGenerator.cs | 11 ++-- .../src/LoggerMessageAttribute.cs | 32 +++++------ .../Baselines/TestWithOneParam.generated.txt | 2 +- ...Extensions.Logging.Generators.Tests.csproj | 11 +--- 6 files changed, 77 insertions(+), 38 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index c329a43ed89b5..e1e95735fd12e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -24,6 +24,7 @@ internal class Emitter public string Emit(IReadOnlyList logClasses, CancellationToken cancellationToken) { + _builder.Clear(); _builder.AppendLine("// "); _builder.AppendLine("#nullable enable"); @@ -36,12 +37,54 @@ public string Emit(IReadOnlyList logClasses, CancellationToken canc return _builder.ToString(); } - private static string EscapeMessageString(string message) + private static string ConvertEndOfLineAndQutationCharactersToEscapeForm(string s) { - return message - .ReplaceOrdinal("\n", "\\n") - .ReplaceOrdinal("\r", "\\r") - .ReplaceOrdinal("\"", "\\\""); + int index = 0; + while (index < s.Length) + { + if (s[index] == '\n' || s[index] == '\r' || s[index] == '"') + { + break; + } + index++; + } + + if (index >= s.Length) + { + return s; + } + + StringBuilder sb = new StringBuilder(s.Length); + sb.Append(s, 0, index); + + while (index < s.Length) + { + switch (s[index]) + { + case '\n': + sb.Append('\\'); + sb.Append('n'); + break; + + case '\r': + sb.Append('\\'); + sb.Append('r'); + break; + + case '"': + sb.Append('\\'); + sb.Append('"'); + break; + + default: + sb.Append(s[index]); + break; + } + + index++; + } + + return sb.ToString(); } private static bool UseLoggerMessageDefine(LoggerMethod lm) @@ -225,7 +268,7 @@ private void GenCases(LoggerMethod lm) _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),"); } - _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{EscapeMessageString(lm.Message)}\"),"); + _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{ConvertEndOfLineAndQutationCharactersToEscapeForm(lm.Message)}\"),"); } private void GenCallbackArguments(LoggerMethod lm) @@ -347,7 +390,7 @@ private void GenLogMethod(LoggerMethod lm) GenDefineTypes(lm, brackets: true); - _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{EscapeMessageString(lm.Message)}""); + _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{ConvertEndOfLineAndQutationCharactersToEscapeForm(lm.Message)}"", true); "); } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 4dccac129c311..95e1452761b16 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -653,6 +653,8 @@ internal class LoggerParameter public bool IsException; public bool IsLogLevel; public bool IsEnumerable; + // A parameter flagged as IsTemplateParameter is not going to be taken care of specially as an argument to ILogger.Log + // but instead is supposed to be taken as a parameter for the template. public bool IsTemplateParameter => !IsLogger && !IsException && !IsLogLevel; } } diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs index 0c2f42ea4562e..8d688ffe54ff6 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.cs @@ -34,11 +34,14 @@ public void Execute(GeneratorExecutionContext context) } var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken); - var e = new Emitter(); IReadOnlyList logClasses = p.GetLogClasses(receiver.ClassDeclarations); - string result = e.Emit(logClasses, context.CancellationToken); - - context.AddSource(nameof(LoggerMessageGenerator), SourceText.From(result, Encoding.UTF8)); + if (logClasses.Count > 0) + { + var e = new Emitter(); + string result = e.Emit(logClasses, context.CancellationToken); + + context.AddSource(nameof(LoggerMessageGenerator), SourceText.From(result, Encoding.UTF8)); + } } [ExcludeFromCodeCoverage] diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs index 081e4bfe6ea84..c9c39716ab330 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs @@ -8,6 +8,22 @@ namespace Microsoft.Extensions.Logging /// /// Provides information to guide the production of a strongly-typed logging method. /// + /// + /// The method this attribute is applied to: + /// - Must be a partial method. + /// - Must return void. + /// - Must not be generic. + /// - Must have an as one of its parameters. + /// - Must have a as one of its parameters. + /// - None of the parameters can be generic. + /// + /// + /// static partial class Log + /// { + /// [LoggerMessage(EventId = 0, Message = "Could not open socket for {hostName}")] + /// static partial void CouldNotOpenSocket(ILogger logger, LogLevel level, string hostName); + /// } + /// [AttributeUsage(AttributeTargets.Method)] public sealed class LoggerMessageAttribute : Attribute { @@ -15,22 +31,6 @@ public sealed class LoggerMessageAttribute : Attribute /// Initializes a new instance of the class /// which is used to guide the production of a strongly-typed logging method. /// - /// - /// The method this attribute is applied to: - /// - Must be a partial method. - /// - Must return void. - /// - Must not be generic. - /// - Must have an as one of its parameters. - /// - Must have a as one of its parameters. - /// - None of the parameters can be generic. - /// - /// - /// static partial class Log - /// { - /// [LoggerMessage(EventId = 0, Message = "Could not open socket for {hostName}")] - /// static partial void CouldNotOpenSocket(ILogger logger, LogLevel level, string hostName); - /// } - /// public LoggerMessageAttribute() { } /// diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt index 01824e4f52286..1f4fb2ea13c28 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] private static readonly global::System.Action __M0Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {A1}"); + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {A1}", true); [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] public static partial void M0(Microsoft.Extensions.Logging.ILogger logger, int a1) diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index d37d790d0777d..3efef108b1fc0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -23,16 +23,7 @@ PreserveNewest - - - - - PreserveNewest - - - PreserveNewest - - + PreserveNewest From 1b7a1583d3c77429305bf8427a8c74e6eed2e916 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 11:12:15 -0700 Subject: [PATCH 114/121] Revert "Upgrade generator package versions" This reverts commit b970fdd49228229249d9268cb65dfa27a7c4839c. --- eng/Versions.props | 4 ++-- .../Microsoft.Extensions.Logging.Generators.Tests.csproj | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index c6584bcb808e4..c13010725ce84 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,8 +46,8 @@ - 3.10.0-1.final - 3.10.0-1.final + 3.8.0 + 3.8.0 6.0.0-preview3.21168.1 diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 3efef108b1fc0..5e3c121be0a3f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -4,7 +4,6 @@ $(NetCoreAppCurrent) true true - 3.10.0-1.final From d94e42f3b8c5c28ca6707300860bdc82f8cc3b8c Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 11:28:55 -0700 Subject: [PATCH 115/121] Block range for logging in MD file --- docs/project/list-of-diagnostics.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 4e0069152591c..df1f73e6d7b06 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -53,4 +53,5 @@ Currently the identifiers `SYSLIB0001` through `SYSLIB0999` are carved out for o | __`SYSLIB1020`__ | Found multiple fields of type Microsoft.Extensions.Logging.ILogger | | __`SYSLIB1021`__ | Can't have the same template with different casing | | __`SYSLIB1022`__ | Can't have malformed format strings (like dangling {, etc) | -| __`SYSLIB1023`__ | Generating more than 6 arguments is not supported | \ No newline at end of file +| __`SYSLIB1023`__ | Generating more than 6 arguments is not supported | +| __`SYSLIB1029`__ | *_Blocked range `SYSLIB1024`-`SYSLIB1029` for logging._* | \ No newline at end of file From a2f18d92eb158c2c616eaadda21d799b02d5a191 Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 20:06:40 -0700 Subject: [PATCH 116/121] enable nullable on test project --- .../tests/SourceGenerators/RoslynTestUtils.cs | 14 +++++++------- ...soft.Extensions.Logging.Generators.Tests.csproj | 1 + .../MockLogger.cs | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs index b14572bf49405..41c6239d5f002 100644 --- a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs +++ b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs @@ -64,7 +64,7 @@ public static async Task AssertNoDiagnostic(this Project proj, params string[] i { foreach (Document doc in proj.Documents) { - SemanticModel sm = await doc.GetSemanticModelAsync(CancellationToken.None).ConfigureAwait(false); + SemanticModel? sm = await doc.GetSemanticModelAsync(CancellationToken.None).ConfigureAwait(false); Assert.NotNull(sm); foreach (Diagnostic d in sm!.GetDiagnostics()) @@ -139,7 +139,7 @@ public static TextSpan MakeSpan(string text, int spanNum) Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution)); - Compilation comp = await proj!.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false); + Compilation? comp = await proj!.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false); CSharpGeneratorDriver cgd = CSharpGeneratorDriver.Create(new[] { generator }, optionsProvider: optionsProvider); GeneratorDriver gd = cgd.RunGenerators(comp!, cancellationToken); @@ -168,7 +168,7 @@ public static async Task> RunAnalyzer( ImmutableArray analyzers = ImmutableArray.Create(analyzer); - Compilation comp = await proj!.GetCompilationAsync().ConfigureAwait(false); + Compilation? comp = await proj!.GetCompilationAsync().ConfigureAwait(false); return await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false); } @@ -214,7 +214,7 @@ public static async Task> RunAnalyzerAndFixer( while (true) { - Compilation comp = await proj!.GetCompilationAsync().ConfigureAwait(false); + Compilation? comp = await proj!.GetCompilationAsync().ConfigureAwait(false); ImmutableArray diags = await comp!.WithAnalyzers(analyzers).GetAllDiagnosticsAsync().ConfigureAwait(false); if (diags.IsEmpty) { @@ -225,7 +225,7 @@ public static async Task> RunAnalyzerAndFixer( var actions = new List(); foreach (Diagnostic d in diags) { - Document doc = proj.GetDocument(d.Location.SourceTree); + Document? doc = proj.GetDocument(d.Location.SourceTree); CodeFixContext context = new CodeFixContext(doc!, d, (action, _) => actions.Add(action), CancellationToken.None); await fixer.RegisterCodeFixesAsync(context).ConfigureAwait(false); @@ -239,7 +239,7 @@ public static async Task> RunAnalyzerAndFixer( ImmutableArray operations = await actions[0].GetOperationsAsync(CancellationToken.None).ConfigureAwait(false); Solution solution = operations.OfType().Single().ChangedSolution; - Project changedProj = solution.GetProject(proj.Id); + Project? changedProj = solution.GetProject(proj.Id); if (changedProj != proj) { proj = await RecreateProjectDocumentsAsync(changedProj!).ConfigureAwait(false); @@ -279,7 +279,7 @@ private static async Task RecreateProjectDocumentsAsync(Project project { foreach (DocumentId documentId in project.DocumentIds) { - Document document = project.GetDocument(documentId); + Document? document = project.GetDocument(documentId); document = await RecreateDocumentAsync(document!).ConfigureAwait(false); project = document.Project; } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj index 5e3c121be0a3f..329b67c2a16a4 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Microsoft.Extensions.Logging.Generators.Tests.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent) true true + enable diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs index 97fef16001244..3fa88999b3ab5 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/MockLogger.cs @@ -45,7 +45,7 @@ public bool IsEnabled(LogLevel logLevel) return Enabled; } - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { LastLogLevel = logLevel; LastEventId = eventId; From e110684a4fc3d87961428609060fea6d295d3f9a Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 20:08:45 -0700 Subject: [PATCH 117/121] Add missing "global::" to types --- .../gen/LoggerMessageGenerator.Parser.cs | 4 +++- .../TestWithDynamicLogLevel.generated.txt | 2 +- .../TestWithMoreThan6Params.generated.txt | 18 +++++++++--------- .../Baselines/TestWithOneParam.generated.txt | 6 +++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 95e1452761b16..1819c7449aaf9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -227,7 +227,9 @@ public IReadOnlyList GetLogClasses(IEnumerable> { - private readonly int _p1; - private readonly int _p2; - private readonly int _p3; - private readonly int _p4; - private readonly int _p5; - private readonly int _p6; - private readonly int _p7; + private readonly global::System.Int32 _p1; + private readonly global::System.Int32 _p2; + private readonly global::System.Int32 _p3; + private readonly global::System.Int32 _p4; + private readonly global::System.Int32 _p5; + private readonly global::System.Int32 _p6; + private readonly global::System.Int32 _p7; - public __Method9Struct(int p1, int p2, int p3, int p4, int p5, int p6, int p7) + public __Method9Struct(global::System.Int32 p1, global::System.Int32 p2, global::System.Int32 p3, global::System.Int32 p4, global::System.Int32 p5, global::System.Int32 p6, global::System.Int32 p7) { this._p1 = p1; this._p2 = p2; @@ -74,7 +74,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - public static partial void Method9(Microsoft.Extensions.Logging.ILogger logger, int p1, int p2, int p3, int p4, int p5, int p6, int p7) + public static partial void Method9(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Int32 p1, global::System.Int32 p2, global::System.Int32 p3, global::System.Int32 p4, global::System.Int32 p5, global::System.Int32 p6, global::System.Int32 p7) { if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error)) { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt index 1f4fb2ea13c28..866b0469124d8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithOneParam.generated.txt @@ -6,11 +6,11 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses partial class TestWithOneParam { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - private static readonly global::System.Action __M0Callback = - global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {A1}", true); + private static readonly global::System.Action __M0Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Error, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0 {A1}", true); [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")] - public static partial void M0(Microsoft.Extensions.Logging.ILogger logger, int a1) + public static partial void M0(global::Microsoft.Extensions.Logging.ILogger logger, global::System.Int32 a1) { if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Error)) { From ba9b77fa9b903d405f912008f2060c4fb6a89c7d Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 20:11:33 -0700 Subject: [PATCH 118/121] Fix up triple slash comments for LoggerMessageAttribute --- .../src/LoggerMessageAttribute.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs index c9c39716ab330..b103ef31a4e16 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggerMessageAttribute.cs @@ -9,20 +9,24 @@ namespace Microsoft.Extensions.Logging /// Provides information to guide the production of a strongly-typed logging method. /// /// - /// The method this attribute is applied to: - /// - Must be a partial method. - /// - Must return void. - /// - Must not be generic. - /// - Must have an as one of its parameters. - /// - Must have a as one of its parameters. - /// - None of the parameters can be generic. + /// The method this attribute is applied to: + /// - Must be a partial method. + /// - Must return void. + /// - Must not be generic. + /// - Must have an as one of its parameters. + /// - Must have a as one of its parameters. + /// - None of the parameters can be generic. /// /// + /// /// [AttributeUsage(AttributeTargets.Method)] public sealed class LoggerMessageAttribute : Attribute From 843c0f39e0b50bc3311a5e3f6a2517f8a3e6c71a Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 20:25:59 -0700 Subject: [PATCH 119/121] lock strings --- .../gen/Resources/Strings.resx | 5 +++++ .../gen/Resources/xlf/Strings.cs.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.de.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.es.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.fr.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.it.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.ja.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.ko.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.pl.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.pt-BR.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.ru.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.tr.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.zh-Hans.xlf | 10 +++++----- .../gen/Resources/xlf/Strings.zh-Hant.xlf | 10 +++++----- 14 files changed, 70 insertions(+), 65 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx index e00ea13e8aa01..f0d6b21bf7ae8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/Strings.resx @@ -143,6 +143,7 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface + {Locked="Microsoft.Extensions.Logging.ILogger"} Logging methods must be static @@ -191,15 +192,19 @@ Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger + {Locked="Microsoft.Extensions.Logging.ILogger"} Can't have the same template with different casing diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf index 43553ee95be5f..044e1bd67633f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.cs.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf index d29712e845e01..258f697c492e2 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.de.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf index 8a9dc158a7097..0424989915dd7 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.es.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf index 436d7d00b4d05..a482372783f57 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.fr.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf index dbb0ebc47518c..304056d1bd69f 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.it.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf index 38065be7c9537..c8b172fd7630b 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ja.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf index 1adc3f3f1d839..fdb53576c829a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ko.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf index 66acfb696c33b..790fec15f6a66 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pl.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf index e4e305783e844..1451df01554bb 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.pt-BR.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf index 215ce1eb850c3..66fbc83a6e6a9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.ru.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf index 167819d1e1e04..fc70ba858ab99 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.tr.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf index 5bb70e2e58d86..2cdeaa1133a21 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf index 6677de9ff9ac7..52f65573cda1e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/Microsoft.Extensions.Logging/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -75,17 +75,17 @@ One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface One of the arguments to a logging method must implement the Microsoft.Extensions.Logging.ILogger interface - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Couldn't find a field of type Microsoft.Extensions.Logging.ILogger Couldn't find a field of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Could not find definition for type {0} @@ -100,12 +100,12 @@ Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class {0} - + {Locked="Microsoft.Extensions.Logging.ILogger"} Found multiple fields of type Microsoft.Extensions.Logging.ILogger Found multiple fields of type Microsoft.Extensions.Logging.ILogger - + {Locked="Microsoft.Extensions.Logging.ILogger"} Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level. From 65b1f5111fc69702fc2facc295cfd531c242c10f Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 21:01:59 -0700 Subject: [PATCH 120/121] - Feedback on RoslynTestUtils - Feedback on ConvertEndOfLineAndQuotationCharactersToEscapeForm --- .../tests/SourceGenerators/RoslynTestUtils.cs | 49 ++++---- .../gen/LoggerMessageGenerator.Emitter.cs | 108 ++++++------------ 2 files changed, 60 insertions(+), 97 deletions(-) diff --git a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs index 41c6239d5f002..4928270b06f17 100644 --- a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs +++ b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs @@ -76,6 +76,25 @@ public static async Task AssertNoDiagnostic(this Project proj, params string[] i } } + private static Project WithDocuments(this Project project, IEnumerable sources, IEnumerable? sourceNames = null) + { + int count = 0; + Project result = project; + if (sourceNames != null) + { + List names = sourceNames.ToList(); + foreach (string s in sources) + result = result.WithDocument(names[count++], s); + } + else + { + foreach (string s in sources) + result = result.WithDocument($"src-{count++}.cs", s); + } + + return result; + } + public static Project WithDocument(this Project proj, string name, string text) { return proj.AddDocument(name, text).Project; @@ -131,11 +150,7 @@ public static TextSpan MakeSpan(string text, int spanNum) { Project proj = CreateTestProject(references, includeBaseReferences); - int count = 0; - foreach (string s in sources) - { - proj = proj.WithDocument($"src-{count++}.cs", s); - } + proj = proj.WithDocuments(sources); Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution)); @@ -158,11 +173,7 @@ public static async Task> RunAnalyzer( { Project proj = CreateTestProject(references); - int count = 0; - foreach (string s in sources) - { - proj = proj.WithDocument($"src-{count++}.cs", s); - } + proj = proj.WithDocuments(sources); await proj.CommitChanges().ConfigureAwait(false); @@ -186,22 +197,8 @@ public static async Task> RunAnalyzerAndFixer( { Project proj = CreateTestProject(references); - int count = 0; - if (sourceNames != null) - { - List l = sourceNames.ToList(); - foreach (string s in sources) - { - proj = proj.WithDocument(l[count++], s); - } - } - else - { - foreach (string s in sources) - { - proj = proj.WithDocument($"src-{count++}.cs", s); - } - } + int count = sources.Count(); + proj = proj.WithDocuments(sources, sourceNames); if (defaultNamespace != null) { diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs index e1e95735fd12e..91065fa530fc0 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Emitter.cs @@ -37,56 +37,6 @@ public string Emit(IReadOnlyList logClasses, CancellationToken canc return _builder.ToString(); } - private static string ConvertEndOfLineAndQutationCharactersToEscapeForm(string s) - { - int index = 0; - while (index < s.Length) - { - if (s[index] == '\n' || s[index] == '\r' || s[index] == '"') - { - break; - } - index++; - } - - if (index >= s.Length) - { - return s; - } - - StringBuilder sb = new StringBuilder(s.Length); - sb.Append(s, 0, index); - - while (index < s.Length) - { - switch (s[index]) - { - case '\n': - sb.Append('\\'); - sb.Append('n'); - break; - - case '\r': - sb.Append('\\'); - sb.Append('r'); - break; - - case '"': - sb.Append('\\'); - sb.Append('"'); - break; - - default: - sb.Append(s[index]); - break; - } - - index++; - } - - return sb.ToString(); - } - private static bool UseLoggerMessageDefine(LoggerMethod lm) { bool result = @@ -268,7 +218,7 @@ private void GenCases(LoggerMethod lm) _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{name}\", this._{p.Name}),"); } - _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{ConvertEndOfLineAndQutationCharactersToEscapeForm(lm.Message)}\"),"); + _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair(\"{{OriginalFormat}}\", \"{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}\"),"); } private void GenCallbackArguments(LoggerMethod lm) @@ -390,7 +340,7 @@ private void GenLogMethod(LoggerMethod lm) GenDefineTypes(lm, brackets: true); - _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{ConvertEndOfLineAndQutationCharactersToEscapeForm(lm.Message)}"", true); + _builder.Append(@$"({level}, new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}), ""{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}"", true); "); } @@ -553,36 +503,52 @@ private static string __Enumerate(global::System.Collections.IEnumerable? enumer } } } - } - internal static class StringHelper - { - // Replace operations need to be done with StringComparison.Ordinal - // (Adding code snippet below, since the API taking StringComparison is not available in netstandard2.0) - internal static string ReplaceOrdinal(this string s, string oldValue, string newValue) + private static string ConvertEndOfLineAndQuotationCharactersToEscapeForm(string s) { - int index = s.IndexOf(oldValue, StringComparison.Ordinal); - if (index < 0) + int index = 0; + while (index < s.Length) + { + if (s[index] == '\n' || s[index] == '\r' || s[index] == '"') + { + break; + } + index++; + } + + if (index >= s.Length) { return s; } StringBuilder sb = new StringBuilder(s.Length); sb.Append(s, 0, index); - sb.Append(newValue); - index += oldValue.Length; - int newIndex; - while (index < s.Length && (newIndex = s.IndexOf(oldValue, index, StringComparison.Ordinal)) > 0) + while (index < s.Length) { - sb.Append(s, index, newIndex - index); - sb.Append(newValue); - index = newIndex + oldValue.Length; - } + switch (s[index]) + { + case '\n': + sb.Append('\\'); + sb.Append('n'); + break; - if (index < s.Length) - { - sb.Append(s, index, s.Length - index); + case '\r': + sb.Append('\\'); + sb.Append('r'); + break; + + case '"': + sb.Append('\\'); + sb.Append('"'); + break; + + default: + sb.Append(s[index]); + break; + } + + index++; } return sb.ToString(); From c0c22cefdc3156d902698b0db20b4a6c7e6acabe Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 15 Apr 2021 21:23:15 -0700 Subject: [PATCH 121/121] - Enable nullable on csproj - Add comment in versions.props - Nit code style feedback - Dont keep method when starts with underscore --- eng/Versions.props | 2 +- .../gen/DiagnosticDescriptors.cs | 46 +++++++++---------- .../gen/LoggerMessageGenerator.Parser.cs | 1 + .../gen/LoggerMessageGenerator.cs | 3 +- ...osoft.Extensions.Logging.Generators.csproj | 3 +- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index c13010725ce84..e8e4e3f971a3e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -45,7 +45,7 @@ - + 3.8.0 3.8.0 diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs index 3d8bc17722d49..9e51646b52f17 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/DiagnosticDescriptors.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Logging.Generators { public static class DiagnosticDescriptors { - public static DiagnosticDescriptor InvalidLoggingMethodName { get; } = new ( + public static DiagnosticDescriptor InvalidLoggingMethodName { get; } = new DiagnosticDescriptor( id: "SYSLIB1001", title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -16,7 +16,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ShouldntMentionLogLevelInMessage { get; } = new ( + public static DiagnosticDescriptor ShouldntMentionLogLevelInMessage { get; } = new DiagnosticDescriptor( id: "SYSLIB1002", title: new LocalizableResourceString(nameof(SR.ShouldntMentionLogLevelInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -24,7 +24,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor InvalidLoggingMethodParameterName { get; } = new ( + public static DiagnosticDescriptor InvalidLoggingMethodParameterName { get; } = new DiagnosticDescriptor( id: "SYSLIB1003", title: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.InvalidLoggingMethodParameterNameMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -32,7 +32,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor LoggingMethodInNestedType { get; } = new ( + public static DiagnosticDescriptor LoggingMethodInNestedType { get; } = new DiagnosticDescriptor( id: "SYSLIB1004", title: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -40,7 +40,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor MissingRequiredType { get; } = new ( + public static DiagnosticDescriptor MissingRequiredType { get; } = new DiagnosticDescriptor( id: "SYSLIB1005", title: new LocalizableResourceString(nameof(SR.MissingRequiredTypeTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingRequiredTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -48,7 +48,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ShouldntReuseEventIds { get; } = new ( + public static DiagnosticDescriptor ShouldntReuseEventIds { get; } = new DiagnosticDescriptor( id: "SYSLIB1006", title: new LocalizableResourceString(nameof(SR.ShouldntReuseEventIdsTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ShouldntReuseEventIdsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -56,7 +56,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor LoggingMethodMustReturnVoid { get; } = new ( + public static DiagnosticDescriptor LoggingMethodMustReturnVoid { get; } = new DiagnosticDescriptor( id: "SYSLIB1007", title: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodMustReturnVoidMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -64,7 +64,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor MissingLoggerArgument { get; } = new ( + public static DiagnosticDescriptor MissingLoggerArgument { get; } = new DiagnosticDescriptor( id: "SYSLIB1008", title: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingLoggerArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -72,7 +72,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor LoggingMethodShouldBeStatic { get; } = new ( + public static DiagnosticDescriptor LoggingMethodShouldBeStatic { get; } = new DiagnosticDescriptor( id: "SYSLIB1009", title: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodShouldBeStaticMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -80,7 +80,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor LoggingMethodMustBePartial { get; } = new ( + public static DiagnosticDescriptor LoggingMethodMustBePartial { get; } = new DiagnosticDescriptor( id: "SYSLIB1010", title: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodMustBePartialMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -88,7 +88,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor LoggingMethodIsGeneric { get; } = new ( + public static DiagnosticDescriptor LoggingMethodIsGeneric { get; } = new DiagnosticDescriptor( id: "SYSLIB1011", title: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodIsGenericMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -96,7 +96,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor RedundantQualifierInMessage { get; } = new ( + public static DiagnosticDescriptor RedundantQualifierInMessage { get; } = new DiagnosticDescriptor( id: "SYSLIB1012", title: new LocalizableResourceString(nameof(SR.RedundantQualifierInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.RedundantQualifierInMessageMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -104,7 +104,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor ShouldntMentionExceptionInMessage { get; } = new ( + public static DiagnosticDescriptor ShouldntMentionExceptionInMessage { get; } = new DiagnosticDescriptor( id: "SYSLIB1013", title: new LocalizableResourceString(nameof(SR.ShouldntMentionExceptionInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -112,7 +112,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor TemplateHasNoCorrespondingArgument { get; } = new ( + public static DiagnosticDescriptor TemplateHasNoCorrespondingArgument { get; } = new DiagnosticDescriptor( id: "SYSLIB1014", title: new LocalizableResourceString(nameof(SR.TemplateHasNoCorrespondingArgumentTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.TemplateHasNoCorrespondingArgumentMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -120,7 +120,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ArgumentHasNoCorrespondingTemplate { get; } = new ( + public static DiagnosticDescriptor ArgumentHasNoCorrespondingTemplate { get; } = new DiagnosticDescriptor( id: "SYSLIB1015", title: new LocalizableResourceString(nameof(SR.ArgumentHasNoCorrespondingTemplateTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ArgumentHasNoCorrespondingTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -128,7 +128,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor LoggingMethodHasBody { get; } = new ( + public static DiagnosticDescriptor LoggingMethodHasBody { get; } = new DiagnosticDescriptor( id: "SYSLIB1016", title: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodHasBodyMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -136,7 +136,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor MissingLogLevel { get; } = new ( + public static DiagnosticDescriptor MissingLogLevel { get; } = new DiagnosticDescriptor( id: "SYSLIB1017", title: new LocalizableResourceString(nameof(SR.MissingLogLevelMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingLogLevelMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -144,7 +144,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor ShouldntMentionLoggerInMessage { get; } = new ( + public static DiagnosticDescriptor ShouldntMentionLoggerInMessage { get; } = new DiagnosticDescriptor( id: "SYSLIB1018", title: new LocalizableResourceString(nameof(SR.ShouldntMentionLoggerInMessageTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.ShouldntMentionInTemplateMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -152,7 +152,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true); - public static DiagnosticDescriptor MissingLoggerField { get; } = new ( + public static DiagnosticDescriptor MissingLoggerField { get; } = new DiagnosticDescriptor( id: "SYSLIB1019", title: new LocalizableResourceString(nameof(SR.MissingLoggerFieldTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MissingLoggerFieldMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -160,7 +160,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor MultipleLoggerFields { get; } = new( + public static DiagnosticDescriptor MultipleLoggerFields { get; } = new DiagnosticDescriptor( id: "SYSLIB1020", title: new LocalizableResourceString(nameof(SR.MultipleLoggerFieldsTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MultipleLoggerFieldsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -168,7 +168,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor InconsistentTemplateCasing { get; } = new( + public static DiagnosticDescriptor InconsistentTemplateCasing { get; } = new DiagnosticDescriptor( id: "SYSLIB1021", title: new LocalizableResourceString(nameof(SR.InconsistentTemplateCasingMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.InconsistentTemplateCasingMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -176,7 +176,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor MalformedFormatStrings { get; } = new( + public static DiagnosticDescriptor MalformedFormatStrings { get; } = new DiagnosticDescriptor( id: "SYSLIB1022", title: new LocalizableResourceString(nameof(SR.MalformedFormatStringsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.MalformedFormatStringsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), @@ -184,7 +184,7 @@ public static class DiagnosticDescriptors DiagnosticSeverity.Error, isEnabledByDefault: true); - public static DiagnosticDescriptor GeneratingForMax6Arguments { get; } = new( + public static DiagnosticDescriptor GeneratingForMax6Arguments { get; } = new DiagnosticDescriptor( id: "SYSLIB1023", title: new LocalizableResourceString(nameof(SR.GeneratingForMax6ArgumentsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), messageFormat: new LocalizableResourceString(nameof(SR.GeneratingForMax6ArgumentsMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)), diff --git a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs index 1819c7449aaf9..3b909af90cc6a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging/gen/LoggerMessageGenerator.Parser.cs @@ -139,6 +139,7 @@ public IReadOnlyList GetLogClasses(IEnumerable + netstandard2.0 + enable true false true