1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 17:44:48 +09:00

Add support for primary constructors in LoggerMessageGenerator (#101660)

* Add support for primary constructors in LoggerMessageGenerator

* Get the primary constructor parameters types from the constructor symbol instead of from the semantic model

* Prioritize fields over primary constructor parameters and ignore shadowed parameters when finding a logger

* Make checking for primary constructors non-conditional on Roslyn version and simplify project setup

* Reintroduce Roslyn 4.8 test project

* Add info-level diagnostic for logger primary constructor parameters that are shadowed by field

* Update list of diagnostics with new logging message generator diagnostic

* Only add non-logger field names to set of shadowed names

* Add comment explaining the use of the set of shadowed names with an example
This commit is contained in:
Jacob Bundgaard 2024-05-28 18:13:32 +02:00 committed by GitHub
parent 0005249901
commit 9daa4b41eb
Signed by: github
GPG key ID: B5690EEEBB952194
26 changed files with 533 additions and 33 deletions

View file

@ -145,7 +145,7 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
| __`SYSLIB1024`__ | Argument is using the unsupported out parameter modifier | | __`SYSLIB1024`__ | Argument is using the unsupported out parameter modifier |
| __`SYSLIB1025`__ | Multiple logging methods cannot use the same event name within a class | | __`SYSLIB1025`__ | Multiple logging methods cannot use the same event name within a class |
| __`SYSLIB1026`__ | C# language version not supported by the logging source generator. | | __`SYSLIB1026`__ | C# language version not supported by the logging source generator. |
| __`SYSLIB1027`__ | _`SYSLIB1001`-`SYSLIB1029` reserved for logging._ | | __`SYSLIB1027`__ | Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field |
| __`SYSLIB1028`__ | _`SYSLIB1001`-`SYSLIB1029` reserved for logging._ | | __`SYSLIB1028`__ | _`SYSLIB1001`-`SYSLIB1029` reserved for logging._ |
| __`SYSLIB1029`__ | _`SYSLIB1001`-`SYSLIB1029` reserved for logging._ | | __`SYSLIB1029`__ | _`SYSLIB1001`-`SYSLIB1029` reserved for logging._ |
| __`SYSLIB1030`__ | JsonSourceGenerator did not generate serialization metadata for type | | __`SYSLIB1030`__ | JsonSourceGenerator did not generate serialization metadata for type |

View file

@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Loggin
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators.Roslyn4.0.Tests", "tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Roslyn4.0.Tests.csproj", "{1CB869A7-2EEC-4A53-9C33-DF9E0C75825B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators.Roslyn4.0.Tests", "tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Roslyn4.0.Tests.csproj", "{1CB869A7-2EEC-4A53-9C33-DF9E0C75825B}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.Generators.Roslyn4.8.Tests", "tests\Microsoft.Extensions.Logging.Generators.Tests\Microsoft.Extensions.Logging.Generators.Roslyn4.8.Tests.csproj", "{D6167506-0671-46A3-94E5-7A98032DCEC6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{852D4E16-58C3-47C2-A6BC-A5B12B37209F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{852D4E16-58C3-47C2-A6BC-A5B12B37209F}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{6645D0C4-83D1-4426-B9CD-67096CB7A60F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{6645D0C4-83D1-4426-B9CD-67096CB7A60F}"
@ -135,6 +137,10 @@ Global
{F3186815-B9A5-455F-B0DF-E39D4235C24F}.Debug|Any CPU.Build.0 = Debug|Any CPU {F3186815-B9A5-455F-B0DF-E39D4235C24F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3186815-B9A5-455F-B0DF-E39D4235C24F}.Release|Any CPU.ActiveCfg = Release|Any CPU {F3186815-B9A5-455F-B0DF-E39D4235C24F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3186815-B9A5-455F-B0DF-E39D4235C24F}.Release|Any CPU.Build.0 = Release|Any CPU {F3186815-B9A5-455F-B0DF-E39D4235C24F}.Release|Any CPU.Build.0 = Release|Any CPU
{D6167506-0671-46A3-94E5-7A98032DCEC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6167506-0671-46A3-94E5-7A98032DCEC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6167506-0671-46A3-94E5-7A98032DCEC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6167506-0671-46A3-94E5-7A98032DCEC6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -162,6 +168,7 @@ Global
{8215F79E-510B-4CA1-B775-50C47BB58360} = {58760833-B4F5-429D-9ABD-15FDF83E25CD} {8215F79E-510B-4CA1-B775-50C47BB58360} = {58760833-B4F5-429D-9ABD-15FDF83E25CD}
{F3186815-B9A5-455F-B0DF-E39D4235C24F} = {14DFA192-3C7E-4F10-B5FD-3953BC82A6B1} {F3186815-B9A5-455F-B0DF-E39D4235C24F} = {14DFA192-3C7E-4F10-B5FD-3953BC82A6B1}
{14DFA192-3C7E-4F10-B5FD-3953BC82A6B1} = {58760833-B4F5-429D-9ABD-15FDF83E25CD} {14DFA192-3C7E-4F10-B5FD-3953BC82A6B1} = {58760833-B4F5-429D-9ABD-15FDF83E25CD}
{D6167506-0671-46A3-94E5-7A98032DCEC6} = {4DE63935-DCA9-4D63-9C1F-AAE79C89CA8B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {450DA749-CBDC-4BDC-950F-8A491CF59D49} SolutionGuid = {450DA749-CBDC-4BDC-950F-8A491CF59D49}

View file

@ -208,5 +208,13 @@ namespace Microsoft.Extensions.Logging.Generators
category: "LoggingGenerator", category: "LoggingGenerator",
defaultSeverity: DiagnosticSeverity.Error, defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true); isEnabledByDefault: true);
public static DiagnosticDescriptor PrimaryConstructorParameterLoggerHidden { get; } = DiagnosticDescriptorHelper.Create(
id: "SYSLIB1027",
title: new LocalizableResourceString(nameof(SR.PrimaryConstructorParameterLoggerHiddenTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
messageFormat: new LocalizableResourceString(nameof(SR.PrimaryConstructorParameterLoggerHiddenMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
category: "LoggingGenerator",
DiagnosticSeverity.Info,
isEnabledByDefault: true);
} }
} }

View file

@ -630,11 +630,23 @@ namespace Microsoft.Extensions.Logging.Generators
INamedTypeSymbol? classType = sm.GetDeclaredSymbol(classDec, _cancellationToken); INamedTypeSymbol? classType = sm.GetDeclaredSymbol(classDec, _cancellationToken);
INamedTypeSymbol? currentClassType = classType;
bool onMostDerivedType = true; bool onMostDerivedType = true;
while (classType is { SpecialType: not SpecialType.System_Object }) // We keep track of the names of all non-logger fields, since they prevent referring to logger
// primary constructor parameters with the same name. Example:
// partial class C(ILogger logger)
// {
// private readonly object logger = logger;
//
// [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
// public partial void M1(); // The ILogger primary constructor parameter cannot be used here.
// }
HashSet<string> shadowedNames = new(StringComparer.Ordinal);
while (currentClassType is { SpecialType: not SpecialType.System_Object })
{ {
foreach (IFieldSymbol fs in classType.GetMembers().OfType<IFieldSymbol>()) foreach (IFieldSymbol fs in currentClassType.GetMembers().OfType<IFieldSymbol>())
{ {
if (!onMostDerivedType && fs.DeclaredAccessibility == Accessibility.Private) if (!onMostDerivedType && fs.DeclaredAccessibility == Accessibility.Private)
{ {
@ -651,10 +663,52 @@ namespace Microsoft.Extensions.Logging.Generators
return (null, true); return (null, true);
} }
} }
else
{
shadowedNames.Add(fs.Name);
}
} }
onMostDerivedType = false; onMostDerivedType = false;
classType = classType.BaseType; currentClassType = currentClassType.BaseType;
}
// We prioritize fields over primary constructor parameters and avoid warnings if both exist.
if (loggerField is not null)
{
return (loggerField, false);
}
IEnumerable<IMethodSymbol> primaryConstructors = classType.InstanceConstructors
.Where(ic => ic.DeclaringSyntaxReferences
.Any(ds => ds.GetSyntax() is ClassDeclarationSyntax));
foreach (IMethodSymbol primaryConstructor in primaryConstructors)
{
foreach (IParameterSymbol parameter in primaryConstructor.Parameters)
{
if (IsBaseOrIdentity(parameter.Type, loggerSymbol))
{
if (shadowedNames.Contains(parameter.Name))
{
// Accessible fields always shadow primary constructor parameters,
// so we can't use the primary constructor parameter,
// even if the field is not a valid logger.
Diag(DiagnosticDescriptors.PrimaryConstructorParameterLoggerHidden, parameter.Locations[0], classDec.Identifier.Text);
continue;
}
if (loggerField == null)
{
loggerField = parameter.Name;
}
else
{
return (null, true);
}
}
}
} }
return (loggerField, false); return (loggerField, false);

View file

@ -45,7 +45,7 @@ namespace Microsoft.Extensions.Logging.Generators
return; return;
} }
IEnumerable<ClassDeclarationSyntax> distinctClasses = classes.Distinct(); ImmutableHashSet<ClassDeclarationSyntax> distinctClasses = classes.ToImmutableHashSet();
var p = new Parser(compilation, context.ReportDiagnostic, context.CancellationToken); var p = new Parser(compilation, context.ReportDiagnostic, context.CancellationToken);
IReadOnlyList<LoggerClass> logClasses = p.GetLogClasses(distinctClasses); IReadOnlyList<LoggerClass> logClasses = p.GetLogClasses(distinctClasses);

View file

@ -237,4 +237,12 @@
<data name="LoggingUnsupportedLanguageVersionMessageFormat" xml:space="preserve"> <data name="LoggingUnsupportedLanguageVersionMessageFormat" xml:space="preserve">
<value>The Logging source generator is not available in C# {0}. Please use language version {1} or greater.</value> <value>The Logging source generator is not available in C# {0}. Please use language version {1} or greater.</value>
</data> </data>
<data name="PrimaryConstructorParameterLoggerHiddenMessage" xml:space="preserve">
<value>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</value>
<comment>{Locked="Microsoft.Extensions.Logging.ILogger"}</comment>
</data>
<data name="PrimaryConstructorParameterLoggerHiddenTitle" xml:space="preserve">
<value>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</value>
<comment>{Locked="Microsoft.Extensions.Logging.ILogger"}</comment>
</data>
</root> </root>

View file

@ -132,6 +132,16 @@
<target state="translated">Našlo se několik polí typu Microsoft.Extensions.Logging.ILogger</target> <target state="translated">Našlo se několik polí typu Microsoft.Extensions.Logging.ILogger</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Odeberte redundantní kvalifikátor (Informace:, Upozornění:, Chyba: atd.) ze zprávy o protokolování, protože je na zadané úrovni protokolu implicitní.</target> <target state="translated">Odeberte redundantní kvalifikátor (Informace:, Upozornění:, Chyba: atd.) ze zprávy o protokolování, protože je na zadané úrovni protokolu implicitní.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Mehrere Felder vom Typ "Microsoft.Extensions.Logging.ILogger" gefunden</target> <target state="translated">Mehrere Felder vom Typ "Microsoft.Extensions.Logging.ILogger" gefunden</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Entfernen Sie den redundanten Qualifizierer (z. B. "Info:", "Warnung:" oder "Fehler:") aus der Protokollierungsmeldung, weil er auf der angegebenen Protokollebene implizit enthalten ist.</target> <target state="translated">Entfernen Sie den redundanten Qualifizierer (z. B. "Info:", "Warnung:" oder "Fehler:") aus der Protokollierungsmeldung, weil er auf der angegebenen Protokollebene implizit enthalten ist.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Se encontraron varios campos de tipo Microsoft.Extensions.Logging.ILogger</target> <target state="translated">Se encontraron varios campos de tipo Microsoft.Extensions.Logging.ILogger</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Quitar calificadores redundantes (Información:, Advertencia:, Error:, etc.) del mensaje de registro, ya que está implícito en el nivel de registro especificado.</target> <target state="translated">Quitar calificadores redundantes (Información:, Advertencia:, Error:, etc.) del mensaje de registro, ya que está implícito en el nivel de registro especificado.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Plusieurs champs de type Microsoft.Extensions.Logging.ILogger ont été trouvés</target> <target state="translated">Plusieurs champs de type Microsoft.Extensions.Logging.ILogger ont été trouvés</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Supprimez le qualificateur redondant (Info:, Warning:, Error:, etc.) du message de journalisation, car il est implicite dans le niveau de journalisation spécifié.</target> <target state="translated">Supprimez le qualificateur redondant (Info:, Warning:, Error:, etc.) du message de journalisation, car il est implicite dans le niveau de journalisation spécifié.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Sono stati trovati più campi di tipo Microsoft.Extensions.Logging.ILogger</target> <target state="translated">Sono stati trovati più campi di tipo Microsoft.Extensions.Logging.ILogger</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Rimuovere il qualificatore ridondante (Informazioni:, Avviso:, Errore: e così via) dal messaggio di registrazione perché è implicito nel livello di log specificato.</target> <target state="translated">Rimuovere il qualificatore ridondante (Informazioni:, Avviso:, Errore: e così via) dal messaggio di registrazione perché è implicito nel livello di log specificato.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Microsoft.Extensions.Logging.ILogger という種類の複数のフィールドが見つかりました</target> <target state="translated">Microsoft.Extensions.Logging.ILogger という種類の複数のフィールドが見つかりました</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">指定されたログ レベルでは暗黙的であるため、冗長な修飾子 (Info:、Warning:、Error: など) をログ メッセージから削除します。</target> <target state="translated">指定されたログ レベルでは暗黙的であるため、冗長な修飾子 (Info:、Warning:、Error: など) をログ メッセージから削除します。</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Microsoft.Extensions.Logging.ILogger 형식의 필드가 여러 개 있음</target> <target state="translated">Microsoft.Extensions.Logging.ILogger 형식의 필드가 여러 개 있음</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">중복 한정자(정보:, 경고:, 오류: 등)가 지정된 로그 수준에서 암시적이기 때문에 로깅 메시지에서 제거합니다.</target> <target state="translated">중복 한정자(정보:, 경고:, 오류: 등)가 지정된 로그 수준에서 암시적이기 때문에 로깅 메시지에서 제거합니다.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Znaleziono wiele pól typu Microsoft.Extensions.Logging.ILogger</target> <target state="translated">Znaleziono wiele pól typu Microsoft.Extensions.Logging.ILogger</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Usuń nadmiarowy kwalifikator (Info:, Warning:, Error: itp.) z komunikatu rejestrowania, ponieważ jest on domyślny na określonym poziomie dziennika.</target> <target state="translated">Usuń nadmiarowy kwalifikator (Info:, Warning:, Error: itp.) z komunikatu rejestrowania, ponieważ jest on domyślny na określonym poziomie dziennika.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Múltiplos campos encontrados do tipo Microsoft.Extensions.Logging.ILogger</target> <target state="translated">Múltiplos campos encontrados do tipo Microsoft.Extensions.Logging.ILogger</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Remova o qualificador redundante (Info:, Aviso:, Erro:, etc) da mensagem de log, pois está implícito no nível de log especificado.</target> <target state="translated">Remova o qualificador redundante (Info:, Aviso:, Erro:, etc) da mensagem de log, pois está implícito no nível de log especificado.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Обнаружено несколько полей типа Microsoft.Extensions.Logging.ILogger</target> <target state="translated">Обнаружено несколько полей типа Microsoft.Extensions.Logging.ILogger</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Удалите избыточный квалификатор (Info:, Warning:, Error:, и т. п.) из сообщения журнала, поскольку квалификатор подразумевается на указанном уровне ведения журнала.</target> <target state="translated">Удалите избыточный квалификатор (Info:, Warning:, Error:, и т. п.) из сообщения журнала, поскольку квалификатор подразумевается на указанном уровне ведения журнала.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">Microsoft.Extensions.Logging.ILogger türünde birden çok alan bulundu</target> <target state="translated">Microsoft.Extensions.Logging.ILogger türünde birden çok alan bulundu</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">Belirtilen günlük düzeyinde örtük olduğundan gereksiz niteleyiciyi (Bilgi:, Uyarı:, Hata: vb.) günlüğe kaydetme iletisinden kaldırın.</target> <target state="translated">Belirtilen günlük düzeyinde örtük olduğundan gereksiz niteleyiciyi (Bilgi:, Uyarı:, Hata: vb.) günlüğe kaydetme iletisinden kaldırın.</target>

View file

@ -132,6 +132,16 @@
<target state="translated">找到 Microsoft.Extensions.Logging.ILogger 类型的多个字段</target> <target state="translated">找到 Microsoft.Extensions.Logging.ILogger 类型的多个字段</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">从日志记录消息中删除冗余限定符(信息:、警告:、错误: 等),因为其在指定的日志级别中为隐式内容。</target> <target state="translated">从日志记录消息中删除冗余限定符(信息:、警告:、错误: 等),因为其在指定的日志级别中为隐式内容。</target>

View file

@ -132,6 +132,16 @@
<target state="translated">找到多個 Microsoft.Extensions.Logging.ILogger 類型的欄位</target> <target state="translated">找到多個 Microsoft.Extensions.Logging.ILogger 類型的欄位</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note> <note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit> </trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenMessage">
<source>Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</source>
<target state="new">Class '{0}' has a primary constructor parameter of type Microsoft.Extensions.Logging.ILogger that is hidden by a field in the class or a base class, preventing its use</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="PrimaryConstructorParameterLoggerHiddenTitle">
<source>Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</source>
<target state="new">Primary constructor parameter of type Microsoft.Extensions.Logging.ILogger is hidden by a field</target>
<note>{Locked="Microsoft.Extensions.Logging.ILogger"}</note>
</trans-unit>
<trans-unit id="RedundantQualifierInMessageMessage"> <trans-unit id="RedundantQualifierInMessageMessage">
<source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source> <source>Remove redundant qualifier (Info:, Warning:, Error:, etc) from the logging message since it is implicit in the specified log level.</source>
<target state="translated">從記錄訊息中移除備援限定詞 (資訊:、警告:、錯誤: 等等),因為它在指定的記錄層級中為隱含。</target> <target state="translated">從記錄訊息中移除備援限定詞 (資訊:、警告:、錯誤: 等等),因為它在指定的記錄層級中為隱含。</target>

View file

@ -0,0 +1,21 @@
// <auto-generated/>
#nullable enable
namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
{
partial class TestWithLoggerFromPrimaryConstructor
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")]
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, global::System.Exception?> __M0Callback =
global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true });
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")]
public partial void M0()
{
if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Debug))
{
__M0Callback(logger, null);
}
}
}
}

View file

@ -0,0 +1,21 @@
// <auto-generated/>
#nullable enable
namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
{
partial class TestWithLoggerFromPrimaryConstructor
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")]
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, global::System.Exception?> __M0Callback =
global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true });
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")]
public partial void M0()
{
if (_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Debug))
{
__M0Callback(_logger, null);
}
}
}
}

View file

@ -26,7 +26,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests
} }
[Fact] [Fact]
public void FindsLoggerFieldInAnotherParialClass() public void FindsLoggerFieldInAnotherPartialClass()
{ {
var logger = new MockLogger(); var logger = new MockLogger();
@ -36,6 +36,41 @@ namespace Microsoft.Extensions.Logging.Generators.Tests
Assert.Equal("Test.", logger.LastFormattedString); Assert.Equal("Test.", logger.LastFormattedString);
} }
#if ROSLYN4_8_OR_GREATER
[Fact]
public void FindsLoggerInPrimaryConstructorParameter()
{
var logger = new MockLogger();
logger.Reset();
new ClassWithPrimaryConstructor(logger).Test();
Assert.Equal("Test.", logger.LastFormattedString);
}
[Fact]
public void FindsLoggerInPrimaryConstructorParameterInDifferentPartialDeclaration()
{
var logger = new MockLogger();
logger.Reset();
new ClassWithPrimaryConstructorInDifferentPartialDeclaration(logger).Test();
Assert.Equal("Test.", logger.LastFormattedString);
}
[Fact]
public void FindsLoggerInFieldInitializedFromPrimaryConstructorParameter()
{
var logger = new MockLogger();
logger.Reset();
new ClassWithPrimaryConstructor(logger).Test();
Assert.Equal("Test.", logger.LastFormattedString);
}
#endif
[Fact] [Fact]
public void BasicTests() public void BasicTests()
{ {

View file

@ -187,6 +187,40 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
await VerifyAgainstBaselineUsingFile("TestWithNestedClassWithGenericTypesWithAttributes.generated.txt", testSourceCode); await VerifyAgainstBaselineUsingFile("TestWithNestedClassWithGenericTypesWithAttributes.generated.txt", testSourceCode);
} }
#if ROSLYN4_8_OR_GREATER
[Fact]
public async Task TestBaseline_TestWithLoggerFromPrimaryConstructor_Success()
{
string testSourceCode = @"
namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
{
internal partial class TestWithLoggerFromPrimaryConstructor(ILogger logger)
{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M0"")]
public partial void M0();
}
}";
await VerifyAgainstBaselineUsingFile("TestWithLoggerFromPrimaryConstructor.generated.txt", testSourceCode);
}
[Fact]
public async Task TestBaseline_TestWithLoggerInFieldAndFromPrimaryConstructor_UsesField()
{
string testSourceCode = @"
namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
{
internal partial class TestWithLoggerFromPrimaryConstructor(ILogger logger)
{
private readonly ILogger _logger = logger;
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M0"")]
public partial void M0();
}
}";
await VerifyAgainstBaselineUsingFile("TestWithLoggerInFieldAndFromPrimaryConstructor.generated.txt", testSourceCode);
}
#endif
[Fact] [Fact]
public void GenericTypeParameterAttributesAreRetained() public void GenericTypeParameterAttributesAreRetained()
{ {

View file

@ -417,6 +417,142 @@ namespace Microsoft.Extensions.Logging.Generators.Tests
} }
#endif #endif
[Fact]
public async Task FieldOnOtherPartialDeclarationOK()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C
{
private ILogger _logger;
public C(ILogger logger)
{
_logger = logger;
}
}
partial class C
{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
public partial void M1();
}
");
Assert.Empty(diagnostics);
}
#if ROSLYN4_8_OR_GREATER
[Fact]
public async Task PrimaryConstructorOK()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C(ILogger logger)
{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
public partial void M1();
}
");
Assert.Empty(diagnostics);
}
[Fact]
public async Task PrimaryConstructorOnOtherPartialDeclarationOK()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C(ILogger logger);
partial class C
{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
public partial void M1();
}
");
Assert.Empty(diagnostics);
}
[Fact]
public async Task PrimaryConstructorWithDifferentNameLoggerFieldOK()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C(ILogger logger)
{
private readonly ILogger _logger = logger;
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
public partial void M1();
}
");
Assert.Empty(diagnostics);
}
[Fact]
public async Task PrimaryConstructorWithSameNameLoggerFieldOK()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C(ILogger logger)
{
private readonly ILogger logger = logger;
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
public partial void M1();
}
");
Assert.Empty(diagnostics);
}
[Fact]
public async Task PrimaryConstructorLoggerShadowedByField()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C(ILogger logger)
{
private readonly object logger = logger;
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
public partial void M1();
}
");
Assert.Equal(2, diagnostics.Count);
Assert.Equal(DiagnosticDescriptors.PrimaryConstructorParameterLoggerHidden.Id, diagnostics[0].Id);
var lineSpan = diagnostics[0].Location.GetLineSpan();
Assert.Equal(4, lineSpan.StartLinePosition.Line);
Assert.Equal(40, lineSpan.StartLinePosition.Character);
Assert.Equal(DiagnosticDescriptors.MissingLoggerField.Id, diagnostics[1].Id);
}
[Fact]
public async Task PrimaryConstructorLoggerShadowedByBaseClass()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
class Base(object logger) {
protected readonly object logger = logger;
}
partial class Derived(ILogger logger) : Base(logger)
{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")]
public partial void M1();
}
");
Assert.Equal(2, diagnostics.Count);
Assert.Equal(DiagnosticDescriptors.PrimaryConstructorParameterLoggerHidden.Id, diagnostics[0].Id);
var lineSpan = diagnostics[0].Location.GetLineSpan();
Assert.Equal(8, lineSpan.StartLinePosition.Line);
Assert.Equal(46, lineSpan.StartLinePosition.Character);
Assert.Equal(DiagnosticDescriptors.MissingLoggerField.Id, diagnostics[1].Id);
}
#endif
[Theory] [Theory]
[InlineData("false")] [InlineData("false")]
[InlineData("true")] [InlineData("true")]

View file

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RoslynApiVersion>$(MicrosoftCodeAnalysisVersion_4_8)</RoslynApiVersion>
<DefineConstants>$(DefineConstants);ROSLYN4_0_OR_GREATER;ROSLYN4_8_OR_GREATER</DefineConstants>
<IsHighAotMemoryUsageTest>true</IsHighAotMemoryUsageTest>
<EmccLinkOptimizationFlag Condition="'$(ContinuousIntegrationBuild)' == 'true'">-O1</EmccLinkOptimizationFlag>
<!-- this Roslyn version brings in NS1.x dependencies -->
<FlagNetStandard1XDependencies>false</FlagNetStandard1XDependencies>
</PropertyGroup>
<ItemGroup>
<HighAotMemoryUsageAssembly Include="Microsoft.CodeAnalysis.CSharp.dll" />
</ItemGroup>
<Import Project="Microsoft.Extensions.Logging.Generators.targets" />
<ItemGroup>
<ProjectReference Include="..\..\gen\Microsoft.Extensions.Logging.Generators.Roslyn4.4.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
</ItemGroup>
</Project>

View file

@ -57,6 +57,30 @@ public partial class PartialClassWithLoggerField
public partial void Test(); public partial void Test();
} }
#if ROSLYN4_8_OR_GREATER
public partial class ClassWithPrimaryConstructor(ILogger logger)
{
[LoggerMessage(0, LogLevel.Debug, "Test.")]
public partial void Test();
}
public partial class ClassWithPrimaryConstructorInDifferentPartialDeclaration(ILogger logger);
public partial class ClassWithPrimaryConstructorInDifferentPartialDeclaration
{
[LoggerMessage(0, LogLevel.Debug, "Test.")]
public partial void Test();
}
public partial class ClassWithPrimaryConstructorAndField(ILogger logger)
{
private readonly ILogger _logger = logger;
[LoggerMessage(0, LogLevel.Debug, "Test.")]
public partial void Test();
}
#endif
// Used to test use outside of a namespace // Used to test use outside of a namespace
internal static partial class NoNamespace internal static partial class NoNamespace
{ {