diff --git a/README.md b/README.md index c4f77bc9f..0f0717a87 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0121](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0121.md)|Design|Do not overwrite parameter value|ℹ️|❌|❌| |[MA0122](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0122.md)|Design|Parameters with \[SupplyParameterFromQuery\] attributes are only valid in routable components (@page)|ℹ️|✔️|❌| |[MA0123](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0123.md)|Design|Sequence number must be a constant|⚠️|✔️|❌| -|[MA0124](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0124.md)|Design|Log Parameter type is not valid|⚠️|✔️|❌| +|[MA0124](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0124.md)|Design|Log parameter type is not valid|⚠️|✔️|❌| |[MA0125](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0125.md)|Design|The list of log parameter types contains an invalid type|⚠️|✔️|❌| |[MA0126](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0126.md)|Design|The list of log parameter types contains a duplicate|⚠️|✔️|❌| |[MA0127](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0127.md)|Usage|Use String.Equals instead of is pattern|⚠️|❌|❌| @@ -154,7 +154,7 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0136](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0136.md)|Usage|Raw String contains an implicit end of line character|👻|✔️|❌| |[MA0137](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0137.md)|Design|Use 'Async' suffix when a method returns an awaitable type|⚠️|❌|❌| |[MA0138](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0138.md)|Design|Do not use 'Async' suffix when a method does not return an awaitable type|⚠️|❌|❌| -|[MA0139](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0139.md)|Design|Log Parameter type is not valid|⚠️|✔️|❌| +|[MA0139](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0139.md)|Design|Log parameter type is not valid|⚠️|✔️|❌| |[MA0140](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0140.md)|Design|Both if and else branch have identical code|⚠️|✔️|❌| |[MA0141](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0141.md)|Usage|Use pattern matching instead of inequality operators for null check|ℹ️|❌|✔️| |[MA0142](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0142.md)|Usage|Use pattern matching instead of equality operators for null check|ℹ️|❌|✔️| diff --git a/docs/README.md b/docs/README.md index a8cdc17ed..a38324041 100644 --- a/docs/README.md +++ b/docs/README.md @@ -123,7 +123,7 @@ |[MA0121](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0121.md)|Design|Do not overwrite parameter value|ℹ️|❌|❌| |[MA0122](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0122.md)|Design|Parameters with \[SupplyParameterFromQuery\] attributes are only valid in routable components (@page)|ℹ️|✔️|❌| |[MA0123](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0123.md)|Design|Sequence number must be a constant|⚠️|✔️|❌| -|[MA0124](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0124.md)|Design|Log Parameter type is not valid|⚠️|✔️|❌| +|[MA0124](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0124.md)|Design|Log parameter type is not valid|⚠️|✔️|❌| |[MA0125](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0125.md)|Design|The list of log parameter types contains an invalid type|⚠️|✔️|❌| |[MA0126](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0126.md)|Design|The list of log parameter types contains a duplicate|⚠️|✔️|❌| |[MA0127](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0127.md)|Usage|Use String.Equals instead of is pattern|⚠️|❌|❌| @@ -138,7 +138,7 @@ |[MA0136](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0136.md)|Usage|Raw String contains an implicit end of line character|👻|✔️|❌| |[MA0137](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0137.md)|Design|Use 'Async' suffix when a method returns an awaitable type|⚠️|❌|❌| |[MA0138](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0138.md)|Design|Do not use 'Async' suffix when a method does not return an awaitable type|⚠️|❌|❌| -|[MA0139](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0139.md)|Design|Log Parameter type is not valid|⚠️|✔️|❌| +|[MA0139](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0139.md)|Design|Log parameter type is not valid|⚠️|✔️|❌| |[MA0140](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0140.md)|Design|Both if and else branch have identical code|⚠️|✔️|❌| |[MA0141](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0141.md)|Usage|Use pattern matching instead of inequality operators for null check|ℹ️|❌|✔️| |[MA0142](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0142.md)|Usage|Use pattern matching instead of equality operators for null check|ℹ️|❌|✔️| @@ -529,7 +529,7 @@ dotnet_diagnostic.MA0122.severity = suggestion # MA0123: Sequence number must be a constant dotnet_diagnostic.MA0123.severity = warning -# MA0124: Log Parameter type is not valid +# MA0124: Log parameter type is not valid dotnet_diagnostic.MA0124.severity = warning # MA0125: The list of log parameter types contains an invalid type @@ -574,7 +574,7 @@ dotnet_diagnostic.MA0137.severity = none # MA0138: Do not use 'Async' suffix when a method does not return an awaitable type dotnet_diagnostic.MA0138.severity = none -# MA0139: Log Parameter type is not valid +# MA0139: Log parameter type is not valid dotnet_diagnostic.MA0139.severity = warning # MA0140: Both if and else branch have identical code @@ -989,7 +989,7 @@ dotnet_diagnostic.MA0122.severity = none # MA0123: Sequence number must be a constant dotnet_diagnostic.MA0123.severity = none -# MA0124: Log Parameter type is not valid +# MA0124: Log parameter type is not valid dotnet_diagnostic.MA0124.severity = none # MA0125: The list of log parameter types contains an invalid type @@ -1034,7 +1034,7 @@ dotnet_diagnostic.MA0137.severity = none # MA0138: Do not use 'Async' suffix when a method does not return an awaitable type dotnet_diagnostic.MA0138.severity = none -# MA0139: Log Parameter type is not valid +# MA0139: Log parameter type is not valid dotnet_diagnostic.MA0139.severity = none # MA0140: Both if and else branch have identical code diff --git a/docs/Rules/MA0124.md b/docs/Rules/MA0124.md index 159042ab4..d41ee3ae3 100644 --- a/docs/Rules/MA0124.md +++ b/docs/Rules/MA0124.md @@ -1,4 +1,4 @@ -# MA0124 - Log Parameter type is not valid +# MA0124 - Log parameter type is not valid This rule ensures the parameters for Microsoft.Extensions.Logging's logger are of the expected types. You can configure the expected types using a configuration file. @@ -14,6 +14,9 @@ Name;System.String Count;System.Int32;System.Int64 Length;System.Int32;System.Nullable{System.Int32} AccountId;System.Nullable{System.Guid} + +# Deny parameter +InvalidParameter; ```` Then, you need to add the file to the `AdditionalFiles` collection in the `csproj` file: diff --git a/docs/Rules/MA0139.md b/docs/Rules/MA0139.md index c2aecae06..3c22083ff 100644 --- a/docs/Rules/MA0139.md +++ b/docs/Rules/MA0139.md @@ -1,4 +1,4 @@ -# MA0139 - Log Parameter type is not valid +# MA0139 - Log parameter type is not valid This rule ensures the parameters for Serilog's logger are of the expected types. You can configure the expected types using a configuration file. @@ -14,6 +14,9 @@ Name;System.String Count;System.Int32;System.Int64 Length;System.Int32;System.Nullable{System.Int32} AccountId;System.Nullable{System.Guid} + +# Deny parameter +InvalidParameter; ```` Then, you need to add the file to the `AdditionalFiles` collection in the `csproj` file: diff --git a/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs b/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs index d676e6c59..2a31bd149 100644 --- a/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs @@ -21,8 +21,8 @@ public sealed class LoggerParameterTypeAnalyzer : DiagnosticAnalyzer { private static readonly DiagnosticDescriptor Rule = new( RuleIdentifiers.LoggerParameterType, - title: "Log Parameter type is not valid", - messageFormat: "Parameter '{0}' must be of type {1} but is of type '{2}'", + title: "Log parameter type is not valid", + messageFormat: "Log parameter '{0}' {1}", RuleCategories.Design, DiagnosticSeverity.Warning, isEnabledByDefault: true, @@ -31,8 +31,8 @@ public sealed class LoggerParameterTypeAnalyzer : DiagnosticAnalyzer private static readonly DiagnosticDescriptor RuleSerilog = new( RuleIdentifiers.LoggerParameterType_Serilog, - title: "Log Parameter type is not valid", - messageFormat: "Parameter '{0}' must be of type {1} but is of type '{2}'", + title: "Log parameter type is not valid", + messageFormat: "Log parameter '{0}' {1}", RuleCategories.Design, DiagnosticSeverity.Warning, isEnabledByDefault: true, @@ -148,52 +148,57 @@ LoggerConfigurationFile LoadConfiguration() continue; var parts = lineText.Split(';'); - if (parts.Length == 1) - continue; + var keyName = parts[0].Trim(); + if (configuration.ContainsKey(keyName)) + { + errors.Add(Diagnostic.Create(RuleDuplicate, CreateLocation(file, sourceText, line), keyName)); + } - var types = new List(capacity: parts.Length - 1); - for (var i = 1; i < parts.Length; i++) + if (parts.Length > 1) { - var typeName = parts[i].Trim(); - var type = compilation.GetTypesByMetadataName(typeName); - if (type.Length > 0) - { - types.AddRange(type); - } - else + var types = new List(capacity: parts.Length - 1); + for (var i = 1; i < parts.Length; i++) { - var symbols = DocumentationCommentId.GetSymbolsForReferenceId(typeName, compilation); - if (symbols.Length > 0) + var typeName = parts[i].Trim(); + if (string.IsNullOrEmpty(typeName)) + continue; + + var type = compilation.GetTypesByMetadataName(typeName); + if (type.Length > 0) + { + types.AddRange(type); + } + else { - foreach (var symbol in symbols) + var symbols = DocumentationCommentId.GetSymbolsForReferenceId(typeName, compilation); + if (symbols.Length > 0) { - if (symbol.Kind is SymbolKind.NamedType or SymbolKind.ArrayType && symbol is ITypeSymbol typeSymbol) + foreach (var symbol in symbols) { - types.Add(typeSymbol); - } - else - { - errors.Add(Diagnostic.Create(RuleInvalid, CreateLocation(file, sourceText, line), typeName)); + if (symbol.Kind is SymbolKind.NamedType or SymbolKind.ArrayType && symbol is ITypeSymbol typeSymbol) + { + types.Add(typeSymbol); + } + else + { + errors.Add(Diagnostic.Create(RuleInvalid, CreateLocation(file, sourceText, line), typeName)); + } } } - } - else - { - errors.Add(Diagnostic.Create(RuleInvalid, CreateLocation(file, sourceText, line), typeName)); + else + { + errors.Add(Diagnostic.Create(RuleInvalid, CreateLocation(file, sourceText, line), typeName)); + } } } - } - - if (types.Count > 0) - { - var keyName = parts[0]; - if (configuration.ContainsKey(keyName)) - { - errors.Add(Diagnostic.Create(RuleDuplicate, CreateLocation(file, sourceText, line), keyName)); - } configuration[keyName] = [.. types]; } + else + { + configuration[keyName] = []; + } + } } @@ -242,6 +247,7 @@ public void AnalyzeInvocationDeclaration(OperationAnalysisContext context) { var operation = (IInvocationOperation)context.Operation; var containingType = operation.TargetMethod.ContainingType; + var isSerilog = false; IOperation? formatExpression = null; (ITypeSymbol? Symbol, SyntaxNode Location)[]? argumentTypes = null; @@ -302,6 +308,7 @@ static SyntaxNode GetSyntaxNode(IOperation operation, int index) } else if (SerilogLogMethodNames.Contains(operation.TargetMethod.Name) && operation.TargetMethod.ContainingType.IsEqualToAny(SerilogLogSymbol, SerilogILoggerSymbol)) { + isSerilog = true; var templateIndex = FindIndexOfTemplate(operation.Arguments); if (templateIndex != -1) { @@ -339,7 +346,7 @@ static int FindIndexOfTemplate(ImmutableArray arguments) if (operation.Arguments[0].Value.ConstantValue is { HasValue: true, Value: string valueName }) { var value = operation.Arguments[1].Value; - ValidateLogParameter(context, operation.Arguments[0].Value, SerilogPrefixes, valueName, (value.UnwrapImplicitConversionOperations().Type, value.Syntax)); + ValidateLogParameter(context, operation.Arguments[0].Value, SerilogPrefixes, valueName, (value.UnwrapImplicitConversionOperations().Type, value.Syntax), isSerilog: true); } return; @@ -349,7 +356,7 @@ static int FindIndexOfTemplate(ImmutableArray arguments) if (operation.Arguments[0].Value.ConstantValue is { HasValue: true, Value: string valueName }) { var value = operation.Arguments[1].Value; - ValidateLogParameter(context, operation.Arguments[0].Value, SerilogPrefixes, valueName, (value.UnwrapImplicitConversionOperations().Type, value.Syntax)); + ValidateLogParameter(context, operation.Arguments[0].Value, SerilogPrefixes, valueName, (value.UnwrapImplicitConversionOperations().Type, value.Syntax), isSerilog: true); } return; @@ -359,7 +366,7 @@ static int FindIndexOfTemplate(ImmutableArray arguments) if (operation.Arguments[0].Value.ConstantValue is { HasValue: true, Value: string valueName }) { var value = operation.Arguments[1].Value; - ValidateLogParameter(context, operation.Arguments[0].Value, SerilogPrefixes, valueName, (value.UnwrapImplicitConversionOperations().Type, value.Syntax)); + ValidateLogParameter(context, operation.Arguments[0].Value, SerilogPrefixes, valueName, (value.UnwrapImplicitConversionOperations().Type, value.Syntax), isSerilog: true); } return; @@ -370,7 +377,7 @@ static int FindIndexOfTemplate(ImmutableArray arguments) { var value = operation.Arguments[3].Value; var valueType = operation.TargetMethod.TypeArguments[0]; - ValidateLogParameter(context, operation.Arguments[2].Value, SerilogPrefixes, valueName, (valueType, value.Syntax)); + ValidateLogParameter(context, operation.Arguments[2].Value, SerilogPrefixes, valueName, (valueType, value.Syntax), isSerilog: true); } return; @@ -378,34 +385,45 @@ static int FindIndexOfTemplate(ImmutableArray arguments) if (formatExpression is not null && argumentTypes is not null) { - var allowNonConstantFormat = context.Options.GetConfigurationValue(formatExpression, Rule.Id + ".allow_non_constant_formats", defaultValue: true); + var diagnosticDescriptor = isSerilog ? RuleSerilog : Rule; + + var allowNonConstantFormat = context.Options.GetConfigurationValue(formatExpression, diagnosticDescriptor.Id + ".allow_non_constant_formats", defaultValue: true); var format = TryGetFormatText(formatExpression, allowNonConstantFormat); if (format is null) return; var logFormat = new LogValuesFormatter(format); - for (var i = 0; i < logFormat.ValueNames.Count && i < argumentTypes.Length; i++) + var i = 0; + for (; i < logFormat.ValueNames.Count && i < argumentTypes.Length; i++) { var name = logFormat.ValueNames[i]; var argumentType = argumentTypes[i]; - ValidateLogParameter(context, formatExpression, potentialNamePrefixes, name, argumentType); + ValidateLogParameter(context, formatExpression, potentialNamePrefixes, name, argumentType, isSerilog); + } + + for (; i < logFormat.ValueNames.Count; i++) + { + var name = logFormat.ValueNames[i]; + ValidateParameterName(context, formatExpression, RemovePrefix(name, potentialNamePrefixes), isSerilog); } } } - private void ValidateLogParameter(OperationAnalysisContext context, IOperation nameOperation, char[]? potentialNamePrefixes, string name, (ITypeSymbol? Symbol, SyntaxNode Location) argumentType) + private void ValidateLogParameter(OperationAnalysisContext context, IOperation nameOperation, char[]? potentialNamePrefixes, string name, (ITypeSymbol? Symbol, SyntaxNode Location) argumentType, bool isSerilog) { name = RemovePrefix(name, potentialNamePrefixes); - ValidateLogParameter(context, nameOperation, name, argumentType); + ValidateLogParameter(context, nameOperation, name, argumentType, isSerilog); } - private void ValidateLogParameter(OperationAnalysisContext context, IOperation nameOperation, string name, (ITypeSymbol? Symbol, SyntaxNode Location) argumentType) + private void ValidateLogParameter(OperationAnalysisContext context, IOperation nameOperation, string name, (ITypeSymbol? Symbol, SyntaxNode Location) argumentType, bool isSerilog) { + ValidateParameterName(context, nameOperation, name, isSerilog); + if (!Configuration.IsValid(context.Compilation, name, argumentType.Symbol, out var ruleFound)) { - var expectedSymbols = Configuration.GetSymbols(name); - var expectedSymbolsStr = string.Join(" or ", expectedSymbols.Select(s => $"'{FormatType(s)}'")); - context.ReportDiagnostic(Rule, argumentType.Location, name, expectedSymbolsStr, FormatType(argumentType.Symbol)); + var expectedSymbols = Configuration.GetSymbols(name) ?? []; + var expectedSymbolsStr = $"must be of type {string.Join(" or ", expectedSymbols.Select(s => $"'{FormatType(s)}'"))} but is of type '{FormatType(argumentType.Symbol)}'"; + context.ReportDiagnostic(isSerilog ? RuleSerilog : Rule, argumentType.Location, name, expectedSymbolsStr); } if (!ruleFound) @@ -414,6 +432,15 @@ private void ValidateLogParameter(OperationAnalysisContext context, IOperation n } } + private void ValidateParameterName(OperationAnalysisContext context, IOperation nameOperation, string name, bool isSerilog) + { + var expectedSymbols = Configuration.GetSymbols(name); + if (expectedSymbols is []) + { + context.ReportDiagnostic(isSerilog ? RuleSerilog : Rule, nameOperation, name, "is not allowed by configuration"); + } + } + private static string RemovePrefix(string name, char[]? potentialNamePrefixes) { if (potentialNamePrefixes is not null) @@ -553,6 +580,8 @@ public bool IsValid(Compilation compilation, string name, ITypeSymbol? type, out if (configuration.TryGetValue(name, out var validSymbols)) { hasRule = true; + if (validSymbols.Length == 0) + return true; // the diagnostic is reported on the name, not the value if (type is null) return false; @@ -574,12 +603,12 @@ public bool IsValid(Compilation compilation, string name, ITypeSymbol? type, out return true; } - public ISymbol[] GetSymbols(string name) + public ISymbol[]? GetSymbols(string name) { if (configuration.TryGetValue(name, out var symbols)) return symbols; - return []; + return null; } } diff --git a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs index d53f99411..3a3b59bfe 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs @@ -310,7 +310,7 @@ public async Task Logger_LogTrace_InvalidParameterType_NullableReferenceType() await CreateProjectBuilder() .WithSourceCode(SourceCode) #if ROSLYN_4_6_OR_GREATER - .ShouldReportDiagnosticWithMessage("""Parameter 'Prop' must be of type 'global::System.Nullable' but is of type 'global::System.Nullable'""") + .ShouldReportDiagnosticWithMessage("""Log parameter 'Prop' must be of type 'global::System.Nullable' but is of type 'global::System.Nullable'""") #endif .AddAdditionalFile("LoggerParameterTypes.txt", """ Prop;System.Nullable{System.String} @@ -406,6 +406,46 @@ await CreateProjectBuilder() .ShouldReportDiagnosticWithMessage("Log parameter 'Prop' has no configured type") .ValidateAsync(); } + + [Fact] + public async Task DeniedParameter() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +logger.LogInformation([|"{Prop}"|], 2); +logger.LogInformation("{Dummy}", 2); +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Dummy;System.Int32 +Prop; +""") + .ShouldReportDiagnosticWithMessage("Log parameter 'Prop' is not allowed by configuration") + .ValidateAsync(); + } + + [Fact] + public async Task DeniedParameterWithoutSemiColon() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +logger.LogInformation([|"{Prop}"|], 2); +logger.LogInformation("{Dummy}", 2); +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Dummy;System.Int32 +Prop +""") + .ShouldReportDiagnosticWithMessage("Log parameter 'Prop' is not allowed by configuration") + .ValidateAsync(); + } [Fact] public async Task ConfigurationFromAttribute() diff --git a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs index 3d117b19b..82805540e 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs @@ -61,7 +61,7 @@ await CreateProjectBuilder() Prop;System.Int32 """) .ValidateAsync(); - } + } [Fact] public async Task SeriLog_Log_Information_AtPrefix() @@ -78,7 +78,7 @@ await CreateProjectBuilder() """) .ValidateAsync(); } - + [Fact] public async Task SeriLog_Log_Information_DollarPrefix() { @@ -120,6 +120,7 @@ public async Task SeriLog_Enrich_WithProperty_Invalid() new LoggerConfiguration().Enrich.WithProperty("Prop", [||]""); """; await CreateProjectBuilder() + .WithDefaultAnalyzerId("MA0139") .WithSourceCode(SourceCode) .AddAdditionalFile("LoggerParameterTypes.txt", """ Prop;System.Int32