From a7b309dd099a2c53faa695f52f6e2fc52eecac19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 6 Sep 2026 01:20:02 -0400 Subject: [PATCH] Add tests for the .editorconfig options that were never set by a test Ten documented options were not exercised by any test, so a renamed key, a mis-parsed value, or a flipped default would have gone unnoticed until users hit it in their own repositories. Each option now has a test for both directions: the default behavior and the behavior once the option is set to the other value. - MA0003.excluded_methods - MA0011.exclude_tostring_methods - MA0045.enable_db_special_cases - MA0045.enable_sqlite_special_cases - MA0053.class_with_virtual_member_should_be_sealed - MA0075.consider_nullable_types - MA0075.exclude_tostring_methods - MA0076.consider_nullable_types - MA0104.only_consider_public_symbols - MA0139.allow_non_constant_formats MA0045 and MA0053 pair a documented key with another one, so their tests also pin the precedence: MA0045.enable_*_special_cases overrides the MA0042 key, and the documented class_with_virtual_member_should_be_sealed overrides the hidden misspelled class_with_virtual_member_shoud_be_sealed. --- .../Rules/ClassMustBeSealedAnalyzerTests.cs | 32 +++++ ...BlockingCallInAsyncContextAnalyzerTests.cs | 112 ++++++++++++++++++ ...itCultureSensitiveToStringAnalyzerTests.cs | 87 ++++++++++++++ .../DotNotUseNameFromBCLAnalyzerTests.cs | 19 +++ ...oggerParameterTypeAnalyzer_SerilogTests.cs | 35 ++++++ .../Rules/NamedParameterAnalyzerTests.cs | 40 +++++++ .../Rules/UseIFormatProviderAnalyzerTests.cs | 33 ++++++ 7 files changed, 358 insertions(+) diff --git a/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs index 1cc88bbc8..45412a8c1 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs @@ -196,6 +196,38 @@ internal class {|MA0053:SampleException|} return test.RunAsync(); } + [Fact] + public Task VirtualMember_EditorConfig_DocumentedConfigurationName() + { + var test = CreateTest(); + test.TestState.SetConfiguration("MA0053.class_with_virtual_member_should_be_sealed", "true"); + test.TestCode = """ + internal class {|MA0053:SampleException|} + { + protected virtual void A() => throw null; + } + """; + + return test.RunAsync(); + } + + [Fact] + public Task VirtualMember_EditorConfig_DocumentedConfigurationNameOverridesLegacyOne() + { + var test = CreateTest(); + test.TestState.SetConfiguration( + ("MA0053.class_with_virtual_member_shoud_be_sealed", "true"), + ("MA0053.class_with_virtual_member_should_be_sealed", "false")); + test.TestCode = """ + internal class SampleException + { + protected virtual void A() => throw null; + } + """; + + return test.RunAsync(); + } + [Fact] public Task ComImport() { diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzerTests.cs index 9fae7c8d9..4edb9aa44 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzerTests.cs @@ -4170,6 +4170,52 @@ private void A(SqliteConnection connection) return test.RunAsync(); } + [Fact] + [Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1121")] + public Task PrivateNonAsync_SqliteConnection_Close_MA0045OptionDisabled_Diagnostic() + { + var test = new AnalyzerTest(); + test.TestState.SetConfiguration("MA0045.enable_sqlite_special_cases", "false"); + test.ReferenceAssemblies = test.ReferenceAssemblies.AddSqlite(); + test.TestCode = """ + using Microsoft.Data.Sqlite; + + class Test + { + private void A(SqliteConnection connection) + { + {|MA0045:connection.Close()|}; + } + } + """; + + return test.RunAsync(); + } + + [Fact] + [Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1121")] + public Task PrivateNonAsync_SqliteConnection_Close_MA0045OptionEnabledOverridesMA0042Option_NoDiagnostic() + { + var test = new AnalyzerTest(); + test.TestState.SetConfiguration( + ("MA0042.enable_sqlite_special_cases", "false"), + ("MA0045.enable_sqlite_special_cases", "true")); + test.ReferenceAssemblies = test.ReferenceAssemblies.AddSqlite(); + test.TestCode = """ + using Microsoft.Data.Sqlite; + + class Test + { + private void A(SqliteConnection connection) + { + connection.Close(); + } + } + """; + + return test.RunAsync(); + } + [Fact] [Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1121")] public Task PrivateNonAsync_SqliteCommand_Prepare_OptionDisabled_Diagnostic() @@ -4298,6 +4344,72 @@ class MyDbTransaction : DbTransaction return test.RunAsync(); } + [Fact] + [Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1134")] + public Task PrivateNonAsync_UsingFactoryMethod_DbTransaction_NoDisposeAsyncOverride_MA0045OptionDisabled_Diagnostic() + { + var test = new AnalyzerTest(); + test.TestState.SetConfiguration("MA0045.enable_db_special_cases", "false"); + test.TestCode = """ + using System.Data; + using System.Data.Common; + + class Test + { + private void A() + { + {|MA0045:using var transaction = CreateTransaction();|} + } + + private MyDbTransaction CreateTransaction() => throw null; + } + + class MyDbTransaction : DbTransaction + { + protected override DbConnection DbConnection => throw null; + public override IsolationLevel IsolationLevel => throw null; + public override void Commit() => throw null; + public override void Rollback() => throw null; + } + """; + + return test.RunAsync(); + } + + [Fact] + [Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/1134")] + public Task PrivateNonAsync_UsingFactoryMethod_DbTransaction_NoDisposeAsyncOverride_MA0045OptionEnabledOverridesMA0042Option_NoDiagnostic() + { + var test = new AnalyzerTest(); + test.TestState.SetConfiguration( + ("MA0042.enable_db_special_cases", "false"), + ("MA0045.enable_db_special_cases", "true")); + test.TestCode = """ + using System.Data; + using System.Data.Common; + + class Test + { + private void A() + { + using var transaction = CreateTransaction(); + } + + private MyDbTransaction CreateTransaction() => throw null; + } + + class MyDbTransaction : DbTransaction + { + protected override DbConnection DbConnection => throw null; + public override IsolationLevel IsolationLevel => throw null; + public override void Commit() => throw null; + public override void Rollback() => throw null; + } + """; + + return test.RunAsync(); + } + [Fact] public Task ExcludeFromBlockingCallAnalysisAttribute_DocumentationIdMethod_NonAsyncContext() { diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs index 73e56b7f3..0f569c489 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs @@ -401,6 +401,93 @@ class Test return test.RunAsync(); } + [Fact] + public Task StringConcat_ToString_Int32() + { + var test = CreateTest(); + test.TestCode = """ + class Test + { + void ToString() { _ = "abc" + -1; } + } + """; + + return test.RunAsync(); + } + + [Fact] + public Task StringConcat_ToString_Int32_ConfigNotExcludeToString() + { + var test = CreateTest(); + test.TestState.SetConfiguration("MA0075.exclude_tostring_methods", "false"); + test.TestCode = """ + class Test + { + void ToString() { _ = "abc" + {|MA0075:-1|}; } + } + """; + + return test.RunAsync(); + } + + [Fact] + public Task StringConcat_NullableInt32() + { + var test = CreateTest(); + test.TestCode = """ + class Test + { + void A() { _ = "abc" + {|MA0075:(int?)-1|}; } + } + """; + + return test.RunAsync(); + } + + [Fact] + public Task StringConcat_NullableInt32_ConfigNotConsiderNullableTypes() + { + var test = CreateTest(); + test.TestState.SetConfiguration("MA0075.consider_nullable_types", "false"); + test.TestCode = """ + class Test + { + void A() { _ = "abc" + (int?)-1; } + } + """; + + return test.RunAsync(); + } + + [Fact] + public Task StringInterpolation_NullableInt32() + { + var test = CreateTest(); + test.TestCode = """ + class Test + { + void A() { _ = $"{|MA0076:{(int?)-1}|}"; } + } + """; + + return test.RunAsync(); + } + + [Fact] + public Task StringInterpolation_NullableInt32_ConfigNotConsiderNullableTypes() + { + var test = CreateTest(); + test.TestState.SetConfiguration("MA0076.consider_nullable_types", "false"); + test.TestCode = """ + class Test + { + void A() { _ = $"{(int?)-1}"; } + } + """; + + return test.RunAsync(); + } + [Fact] public Task ObjectToString() { diff --git a/tests/Meziantou.Analyzer.Test/Rules/DotNotUseNameFromBCLAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DotNotUseNameFromBCLAnalyzerTests.cs index 696d08461..815fb221b 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DotNotUseNameFromBCLAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DotNotUseNameFromBCLAnalyzerTests.cs @@ -60,6 +60,25 @@ public Task NestedType_DoNotReportDiagnostic() return test.RunAsync(); } + [Fact] + public Task InternalType_DoNotReportDiagnostic() + { + var test = CreateTest(); + test.TestCode = "internal class Action { }"; + + return test.RunAsync(); + } + + [Fact] + public Task InternalType_ConsiderNonPublicSymbols_ReportDiagnostic() + { + var test = CreateTest(); + test.TestState.SetConfiguration("MA0104.only_consider_public_symbols", "false"); + test.TestCode = "internal class {|MA0104:Action|} { }"; + + return test.RunAsync(); + } + [Fact] public Task Regex_DoNotReportDiagnostic() { diff --git a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs index 310a18b06..add322aae 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs @@ -49,6 +49,41 @@ public Task SeriLog_Log_Information_Exception() return test.RunAsync(); } + [Fact] + public Task SeriLog_Log_Information_StringConcat() + { + var test = CreateTest(); + test.TestCode = """ + using Serilog; + + var a = "test"; + Log.Information("{Prop} " + a, {|MA0139:(int?)1|}); + """; + test.TestState.AdditionalFiles.Add(("LoggerParameterTypes.txt", """ + Prop;System.Int32 + """)); + + return test.RunAsync(); + } + + [Fact] + public Task SeriLog_Log_Information_StringConcat_NonConstantDisabled() + { + var test = CreateTest(); + test.TestState.SetConfiguration("MA0139.allow_non_constant_formats", "false"); + test.TestCode = """ + using Serilog; + + var a = "test"; + Log.Information("{Prop} " + a, (int?)1); + """; + test.TestState.AdditionalFiles.Add(("LoggerParameterTypes.txt", """ + Prop;System.Int32 + """)); + + return test.RunAsync(); + } + [Fact] public Task SeriLog_Log_Information_Params() { diff --git a/tests/Meziantou.Analyzer.Test/Rules/NamedParameterAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/NamedParameterAnalyzerTests.cs index 6475aa1d1..8cc6163b7 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/NamedParameterAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/NamedParameterAnalyzerTests.cs @@ -400,6 +400,46 @@ void MyMethod(int a, long b, short c) { } return test.RunAsync(); } + [Fact] + public Task Int32_ExcludedMethodDeclarationId_ShouldNotReportDiagnostic() + { + var test = CreateTest(); + test.TestState.SetConfiguration(("MA0003.expression_kinds", "numeric"), ("MA0003.excluded_methods", "M:Other.Method|M:TypeName.MyMethod(System.Int32,System.Int64,System.Int16)")); + test.TestCode = """ + class TypeName + { + public void Test() + { + MyMethod(1, 1L, 3); + } + + void MyMethod(int a, long b, short c) { } + } + """; + + return test.RunAsync(); + } + + [Fact] + public Task Int32_ExcludedMethodDeclarationIdOfAnotherMethod_ShouldReportDiagnostic() + { + var test = CreateTest(); + test.TestState.SetConfiguration(("MA0003.expression_kinds", "numeric"), ("MA0003.excluded_methods", "M:TypeName.MyMethod(System.Int32)")); + test.TestCode = """ + class TypeName + { + public void Test() + { + MyMethod({|MA0003:1|}, {|MA0003:1L|}, {|MA0003:3|}); + } + + void MyMethod(int a, long b, short c) { } + } + """; + + return test.RunAsync(); + } + [Fact] public Task Int32_ExcludedMethodWithInvalidRegex_ShouldReportDiagnostic() { diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs index 5d9ea813c..f497c26d2 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs @@ -328,6 +328,39 @@ public Task NullableInt32ToStringWithoutCultureInfo_DisabledConfig() return test.RunAsync(); } + [Fact] + public Task Int32ToStringWithoutCultureInfo_InToStringMethod() + { + var test = CreateTest(); + test.TestCode = """ + _ = 0; + + class Sample + { + public string ToString(int value) => value.ToString(); + } + """; + + return test.RunAsync(); + } + + [Fact] + public Task Int32ToStringWithoutCultureInfo_InToStringMethod_DisabledConfig() + { + var test = CreateTest(); + test.TestState.SetConfiguration("MA0011.exclude_tostring_methods", "false"); + test.TestCode = """ + _ = 0; + + class Sample + { + public string ToString(int value) => {|MA0011:value.ToString()|}; + } + """; + + return test.RunAsync(); + } + [Fact] public Task CultureInsensitiveTypeAttribute_Assembly() {