diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs index 504008c4c367d..cc8dc0b5aaef2 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs @@ -536,29 +536,33 @@ static bool IsAllowedKind(SyntaxKind kind) => { 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()) { - 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); diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 59c5965fefef3..f3419669a361f 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -14,6 +14,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() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs index 5abecef389906..05984d67570bc 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs @@ -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 {