Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ private Task<Project> 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/");
Expand Down Expand Up @@ -258,11 +262,15 @@ private Task<Project> 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/");
Expand Down
3 changes: 2 additions & 1 deletion tests/Meziantou.Analyzer.Test/Helpers/TargetFramework.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
Loading