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

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader> <resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@ -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
{ {