Finds logger field in related types (#71308)
authorSteve Dunn <steve@dunnhq.com>
Tue, 26 Jul 2022 21:12:21 +0000 (22:12 +0100)
committerGitHub <noreply@github.com>
Tue, 26 Jul 2022 21:12:21 +0000 (14:12 -0700)
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs
src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs
src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs

index 504008c..cc8dc0b 100644 (file)
@@ -536,29 +536,33 @@ namespace Microsoft.Extensions.Logging.Generators
             {
                 string? loggerField = null;
 
-                foreach (MemberDeclarationSyntax m in classDec.Members)
+                INamedTypeSymbol? classType = sm.GetDeclaredSymbol(classDec, _cancellationToken);
+
+                bool onMostDerivedType = true;
+
+                while (classType is { SpecialType: not SpecialType.System_Object })
                 {
-                    if (m is FieldDeclarationSyntax fds)
+                    foreach (IFieldSymbol fs in classType.GetMembers().OfType<IFieldSymbol>())
                     {
-                        foreach (VariableDeclaratorSyntax v in fds.Declaration.Variables)
+                        if (!onMostDerivedType && fs.DeclaredAccessibility == Accessibility.Private)
+                        {
+                            continue;
+                        }
+                        if (IsBaseOrIdentity(fs.Type, loggerSymbol))
                         {
-                            var fs = sm.GetDeclaredSymbol(v, _cancellationToken) as IFieldSymbol;
-                            if (fs != null)
+                            if (loggerField == null)
                             {
-                                if (IsBaseOrIdentity(fs.Type, loggerSymbol))
-                                {
-                                    if (loggerField == null)
-                                    {
-                                        loggerField = v.Identifier.Text;
-                                    }
-                                    else
-                                    {
-                                        return (null, true);
-                                    }
-                                }
+                                loggerField = fs.Name;
+                            }
+                            else
+                            {
+                                return (null, true);
                             }
                         }
                     }
+
+                    onMostDerivedType = false;
+                    classType = classType.BaseType;
                 }
 
                 return (loggerField, false);
index 59c5965..f341966 100644 (file)
@@ -15,6 +15,28 @@ namespace Microsoft.Extensions.Logging.Generators.Tests
     public class LoggerMessageGeneratedCodeTests
     {
         [Fact]
+        public void FindsLoggerFieldInBaseClass()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+
+            new DerivedClass(logger).Test();
+            Assert.Equal("Test.", logger.LastFormattedString);
+        }
+
+        [Fact]
+        public void FindsLoggerFieldInAnotherParialClass()
+        {
+            var logger = new MockLogger();
+
+            logger.Reset();
+
+            new PartialClassWithLoggerField(logger).Test();
+            Assert.Equal("Test.", logger.LastFormattedString);
+        }
+
+        [Fact]
         public void BasicTests()
         {
             var logger = new MockLogger();
index 5abecef..05984d6 100644 (file)
@@ -3,6 +3,60 @@
 
 using Microsoft.Extensions.Logging;
 
+// No explicit tests use the following two types, but the fact
+// that they are here means we exercise a constraint that we
+// exclude private fields of base types.
+// If that logic ever changes, then just by having these two classes
+// will mean that compilation fails with:
+// error SYSLIB1020: Found multiple fields of type Microsoft.Extensions.Logging.ILogger in class DerivedClass_with_private_logger
+public class BaseClassWithPrivateLogger
+{
+    private ILogger _logger;
+
+    public BaseClassWithPrivateLogger(ILogger logger) => _logger = logger;
+}
+
+public partial class DerivedClassWithPrivateLogger : BaseClassWithPrivateLogger
+{
+    private ILogger _logger;
+
+    public DerivedClassWithPrivateLogger(ILogger logger) : base(logger)
+    {
+        _logger = logger;
+    }
+
+    [LoggerMessage(0, LogLevel.Debug, "Test.")]
+    public partial void Test();
+}
+
+public class BaseClass
+{
+    protected ILogger _logger;
+
+    public BaseClass(ILogger logger) => _logger = logger;
+}
+
+public partial class DerivedClass : BaseClass
+{
+    public DerivedClass(ILogger logger) : base(logger) { }
+
+    [LoggerMessage(0, LogLevel.Debug, "Test.")]
+    public partial void Test();
+}
+
+public partial class PartialClassWithLoggerField
+{
+    private ILogger _logger;
+
+    public PartialClassWithLoggerField(ILogger logger) => _logger = logger;
+}
+
+public partial class PartialClassWithLoggerField
+{
+    [LoggerMessage(0, LogLevel.Debug, "Test.")]
+    public partial void Test();
+}
+
 // Used to test use outside of a namespace
 internal static partial class NoNamespace
 {