Skip to content

Commit

Permalink
MA0089: Add support for LastIndexOf (#511)
Browse files Browse the repository at this point in the history
  • Loading branch information
meziantou authored Apr 26, 2023
1 parent 8646563 commit a616828
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
78 changes: 78 additions & 0 deletions src/Meziantou.Analyzer/Rules/OptimizeStartsWithAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,29 @@ public AnalyzerContext(Compilation compilation)
IndexOf_Char_StringComparison = method;
}
}

foreach (var method in stringSymbol.GetMembers(nameof(string.LastIndexOf)).OfType<IMethodSymbol>())
{
if (method.IsStatic)
continue;

if (method.Parameters.Length == 1 && method.Parameters[0].Type.IsChar())
{
LastIndexOf_Char = method;
}
else if (method.Parameters.Length == 2 && method.Parameters[0].Type.IsChar() && method.Parameters[1].Type.IsInt32())
{
LastIndexOf_Char_Int32 = method;
}
else if (method.Parameters.Length == 3 && method.Parameters[0].Type.IsChar() && method.Parameters[1].Type.IsInt32() && method.Parameters[2].Type.IsInt32())
{
LastIndexOf_Char_Int32_Int32 = method;
}
else if (method.Parameters.Length == 2 && method.Parameters[0].Type.IsChar() && method.Parameters[1].Type.IsEqualTo(StringComparisonSymbol))
{
LastIndexOf_Char_StringComparison = method;
}
}
}
}

Expand All @@ -110,6 +133,11 @@ public AnalyzerContext(Compilation compilation)
public IMethodSymbol? IndexOf_Char_Int32 { get; set; }
public IMethodSymbol? IndexOf_Char_Int32_Int32 { get; set; }
public IMethodSymbol? IndexOf_Char_StringComparison { get; set; }

public IMethodSymbol? LastIndexOf_Char { get; set; }
public IMethodSymbol? LastIndexOf_Char_Int32 { get; set; }
public IMethodSymbol? LastIndexOf_Char_Int32_Int32 { get; set; }
public IMethodSymbol? LastIndexOf_Char_StringComparison { get; set; }

public INamedTypeSymbol? StringComparisonSymbol { get; set; }
public ISymbol? StringComparison_Ordinal { get; set; }
Expand Down Expand Up @@ -225,6 +253,56 @@ public void AnalyzeInvocation(OperationAnalysisContext context)
}
}
}
else if (operation.TargetMethod.Name is "LastIndexOf")
{
if (operation.Arguments.Length == 2)
{
if (LastIndexOf_Char != null)
{
if (operation.Arguments[0].Value is { Type.SpecialType: SpecialType.System_String, ConstantValue: { HasValue: true, Value: string { Length: 1 } constant } } &&
operation.Arguments[1].Value is { ConstantValue: { HasValue: true, Value: (int)StringComparison.Ordinal } })
{
context.ReportDiagnostic(s_rule, operation);
return;
}
}

if(LastIndexOf_Char_StringComparison != null)
{
if (operation.Arguments[0].Value is { Type.SpecialType: SpecialType.System_String, ConstantValue: { HasValue: true, Value: string { Length: 1 } constant } } &&
operation.Arguments[1].Value.Type.IsEqualTo(StringComparisonSymbol))
{
context.ReportDiagnostic(s_rule, operation);
return;
}
}
}
else if (operation.Arguments.Length == 3)
{
if (LastIndexOf_Char_Int32 == null)
return;

if (operation.Arguments[0].Value is { Type.SpecialType: SpecialType.System_String, ConstantValue: { HasValue: true, Value: string { Length: 1 } constant } } &&
operation.Arguments[1].Value.Type.IsInt32() &&
operation.Arguments[2].Value is { ConstantValue: { HasValue: true, Value: (int)StringComparison.Ordinal } })
{
context.ReportDiagnostic(s_rule, operation);
}
}
else if (operation.Arguments.Length == 4)
{
if (LastIndexOf_Char_Int32_Int32 == null)
return;

if (operation.Arguments[0].Value is { Type.SpecialType: SpecialType.System_String, ConstantValue: { HasValue: true, Value: string { Length: 1 } constant } } &&
operation.Arguments[1].Value.Type.IsInt32() &&
operation.Arguments[2].Value.Type.IsInt32() &&
operation.Arguments[3].Value is { ConstantValue: { HasValue: true, Value: (int)StringComparison.Ordinal } })
{
context.ReportDiagnostic(s_rule, operation);
}
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,72 @@ await CreateProjectBuilder()
.WithTargetFramework(TargetFramework.NetStandard2_0)
.ValidateAsync();
}

[Theory]
[InlineData(@"""a"", StringComparison.Ordinal")]
[InlineData(@"""a"", 1, 2, StringComparison.Ordinal")]
[InlineData(@"""a"", 1, StringComparison.Ordinal")]
public async Task LastIndexOf_Report(string method)
{
var sourceCode = @"
using System;
class Test
{
void A(string str)
{
_ = [||]str.LastIndexOf(" + method + @");
}
}";
await CreateProjectBuilder()
.WithSourceCode(sourceCode)
.ValidateAsync();
}

[Theory]
[InlineData("null")]
[InlineData(@"""""")]
[InlineData(@"str")]
[InlineData(@"""abc""")]
[InlineData(@"""a""")]
[InlineData(@"""a"", 1")]
[InlineData(@"""a"", 1, 2")]
[InlineData(@"""a"", StringComparison.CurrentCulture")]
[InlineData(@"""a"", 1, 2, StringComparison.OrdinalIgnoreCase")]
[InlineData(@"""a"", 1, StringComparison.OrdinalIgnoreCase")]
public async Task LastIndexOf_NoReport(string method)
{
var sourceCode = @"
using System;
class Test
{
void A(string str)
{
_ = str.LastIndexOf(" + method + @");
}
}";
await CreateProjectBuilder()
.WithSourceCode(sourceCode)
.ValidateAsync();
}

[Theory]
[InlineData(@"""a"", StringComparison.OrdinalIgnoreCase")]
public async Task LastIndexOf_NoReport_Netstandard2_0(string method)
{
var sourceCode = @"
using System;
class Test
{
void A(string str)
{
_ = str.LastIndexOf(" + method + @");
}
}";
await CreateProjectBuilder()
.WithSourceCode(sourceCode)
.WithTargetFramework(TargetFramework.NetStandard2_0)
.ValidateAsync();
}

[Theory]
[InlineData(@"""ab"", """"")]
Expand Down

0 comments on commit a616828

Please sign in to comment.