From d75c98fd6681ab02e1c44bf88fe848a80cd45e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 24 May 2026 23:02:22 -0400 Subject: [PATCH 1/3] Prepare for .NET 11 --- .../Helpers/ProjectBuilder.Validation.cs | 10 +++++++++- .../Meziantou.Analyzer.Test/Helpers/TargetFramework.cs | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs index 828b63f29..67fed8268 100755 --- a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs @@ -203,6 +203,10 @@ private Task CreateProject() AddNuGetReference("Microsoft.NETCore.App.Ref", "10.0.0", "ref/net10.0/"); break; + case TargetFramework.Net11_0: + AddNuGetReference("Microsoft.NETCore.App.Ref", "11.0.0-preview.4.26230.115", "ref/net11.0/"); + break; + case TargetFramework.AspNetCore5_0: AddNuGetReference("Microsoft.NETCore.App.Ref", "5.0.0", "ref/net5.0/"); AddNuGetReference("Microsoft.AspNetCore.App.Ref", "5.0.0", "ref/net5.0/"); @@ -258,11 +262,15 @@ private Task CreateProject() case TargetFramework.Net10_0: WithSourceGeneratorsFromNuGet("Microsoft.NETCore.App.Ref", "10.0.0", "analyzers/dotnet/cs/"); break; + + case TargetFramework.Net11_0: + WithSourceGeneratorsFromNuGet("Microsoft.NETCore.App.Ref", "11.0.0-preview.4.26230.115", "analyzers/dotnet/cs/"); + break; } } - if (TargetFramework is not TargetFramework.Net7_0 and not TargetFramework.Net8_0 and not TargetFramework.Net9_0 and not TargetFramework.Net10_0 and not TargetFramework.AspNetCore9_0) + if (TargetFramework is not TargetFramework.Net7_0 and not TargetFramework.Net8_0 and not TargetFramework.Net9_0 and not TargetFramework.Net10_0 and not TargetFramework.Net11_0 and not TargetFramework.AspNetCore9_0) { AddNuGetReference("System.Collections.Immutable", "1.5.0", "lib/netstandard2.0/"); AddNuGetReference("System.Numerics.Vectors", "4.5.0", "ref/netstandard2.0/"); diff --git a/tests/Meziantou.Analyzer.Test/Helpers/TargetFramework.cs b/tests/Meziantou.Analyzer.Test/Helpers/TargetFramework.cs index 29c3364dd..2cea3f7ac 100644 --- a/tests/Meziantou.Analyzer.Test/Helpers/TargetFramework.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/TargetFramework.cs @@ -12,7 +12,8 @@ public enum TargetFramework Net8_0, Net9_0, Net10_0, - NetLatest = Net10_0, + Net11_0, + NetLatest = Net11_0, AspNetCore5_0, AspNetCore6_0, AspNetCore7_0, From efdf0fab5c5353c74fd528929de4a45123f29438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 24 May 2026 23:08:55 -0400 Subject: [PATCH 2/3] Add new methods --- src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs index dd1dc15f0..7d3331a1e 100644 --- a/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs @@ -109,6 +109,11 @@ private bool IsNonCultureSensitiveMethod(IInvocationOperation operation) { Name: nameof(string.Equals), Parameters: [{ Type.SpecialType: SpecialType.System_String }] } or { Name: nameof(string.Equals), IsStatic: true, Parameters: [{ Type.SpecialType: SpecialType.System_String }, { Type.SpecialType: SpecialType.System_String }] } or { Name: nameof(string.IndexOf), Parameters: [{ Type.SpecialType: SpecialType.System_Char }] } or + { Name: nameof(string.IndexOf), Parameters: [{ Type.SpecialType: SpecialType.System_Char }, { Type.SpecialType: SpecialType.System_Int32 }] } or + { Name: nameof(string.IndexOf), Parameters: [{ Type.SpecialType: SpecialType.System_Char }, { Type.SpecialType: SpecialType.System_Int32 }, { Type.SpecialType: SpecialType.System_Int32 }] } or + { Name: nameof(string.LastIndexOf), Parameters: [{ Type.SpecialType: SpecialType.System_Char }] } or + { Name: nameof(string.LastIndexOf), Parameters: [{ Type.SpecialType: SpecialType.System_Char }, { Type.SpecialType: SpecialType.System_Int32 }] } or + { Name: nameof(string.LastIndexOf), Parameters: [{ Type.SpecialType: SpecialType.System_Char }, { Type.SpecialType: SpecialType.System_Int32 }, { Type.SpecialType: SpecialType.System_Int32 }] } or { Name: nameof(string.EndsWith), Parameters: [{ Type.SpecialType: SpecialType.System_Char }] } or { Name: nameof(string.StartsWith), Parameters: [{ Type.SpecialType: SpecialType.System_Char }] } or { Name: nameof(string.Contains), Parameters: [{ Type.SpecialType: SpecialType.System_Char or SpecialType.System_String }] } or From f69d6e9611e30ac7eaaf05561b2913de41dd89b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 24 May 2026 23:39:23 -0400 Subject: [PATCH 3/3] Add tests --- ...parisonAnalyzerNonCultureSensitiveTests.cs | 119 ++++++++++++++++++ .../Rules/UseStringComparisonAnalyzerTests.cs | 68 ++++++++++ 2 files changed, 187 insertions(+) diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerNonCultureSensitiveTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerNonCultureSensitiveTests.cs index 85a9e7c3d..0c17c97ec 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerNonCultureSensitiveTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerNonCultureSensitiveTests.cs @@ -184,6 +184,125 @@ await CreateProjectBuilder() .ValidateAsync(); } + [Fact] + public async Task IndexOf_Char_ShouldReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + [|"a".IndexOf('v')|]; + } + } + """; + const string CodeFix = """ + class TypeName + { + public void Test() + { + "a".IndexOf('v', System.StringComparison.Ordinal); + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ShouldReportDiagnosticWithMessage("Use an overload of 'IndexOf' that has a StringComparison parameter") + .ShouldFixCodeWith(CodeFix) + .ValidateAsync(); + } + + [Fact] + public async Task IndexOf_Char_Int_ShouldReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + [|"abc".IndexOf('v', 0)|]; + } + } + """; + const string CodeFix = """ + class TypeName + { + public void Test() + { + "abc".IndexOf('v', 0, System.StringComparison.Ordinal); + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ShouldReportDiagnosticWithMessage("Use an overload of 'IndexOf' that has a StringComparison parameter") + .ShouldFixCodeWith(CodeFix) + .ValidateAsync(); + } + + [Fact] + public async Task Contains_Char_ShouldReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + [|"abc".Contains('a')|]; + } + } + """; + const string CodeFix = """ + class TypeName + { + public void Test() + { + "abc".Contains('a', System.StringComparison.Ordinal); + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ShouldReportDiagnosticWithMessage("Use an overload of 'Contains' that has a StringComparison parameter") + .ShouldFixCodeWith(CodeFix) + .ValidateAsync(); + } + + [Fact] + public async Task Contains_Char_StringComparison_ShouldNotReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + _ = "abc".Contains('a', System.StringComparison.Ordinal); + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task LastIndexOf_Char_ShouldReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + [|"abc".LastIndexOf('a')|]; + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ShouldReportDiagnosticWithMessage("Use an overload of 'LastIndexOf' that has a StringComparison parameter") + .ValidateAsync(); + } + [Fact] public async Task JObject_Property_ShouldReportDiagnostic() { diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerTests.cs index 57f1de3a4..9f4577a27 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerTests.cs @@ -182,6 +182,74 @@ await CreateProjectBuilder() .ValidateAsync(); } + [Fact] + public async Task IndexOf_Char_ShouldNotReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + _ = "abc".IndexOf('a'); + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task IndexOf_Char_Int_ShouldNotReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + _ = "abc".IndexOf('a', 0); + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task LastIndexOf_Char_ShouldNotReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + _ = "abc".LastIndexOf('a'); + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task Contains_Char_ShouldNotReportDiagnostic() + { + const string SourceCode = """ + class TypeName + { + public void Test() + { + _ = "abc".Contains('a'); + } + } + """; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + [Fact] public async Task Replace_ShouldNotReportDiagnostic() {