Skip to content

Commit

Permalink
MA0002 skips IImmutableSet<string> (#787)
Browse files Browse the repository at this point in the history
  • Loading branch information
meziantou authored Feb 17, 2025
1 parent 7e57a0b commit 0ad62c6
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 12 deletions.
7 changes: 5 additions & 2 deletions src/Meziantou.Analyzer/Internals/AwaitableTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Meziantou.Analyzer.Internals;
internal sealed class AwaitableTypes
{
private readonly INamedTypeSymbol[] _taskOrValueTaskSymbols;
private readonly Compilation _compilation;

public AwaitableTypes(Compilation compilation)
{
Expand All @@ -30,6 +31,8 @@ public AwaitableTypes(Compilation compilation)
{
_taskOrValueTaskSymbols = [];
}

_compilation = compilation;
}

private INamedTypeSymbol? TaskSymbol { get; }
Expand Down Expand Up @@ -74,7 +77,7 @@ public bool IsAwaitable(ITypeSymbol? symbol, SemanticModel semanticModel, int po
return false;
}

public bool IsAwaitable(ITypeSymbol? symbol, Compilation compilation)
public bool IsAwaitable(ITypeSymbol? symbol)
{
if (symbol is null)
return false;
Expand All @@ -95,7 +98,7 @@ public bool IsAwaitable(ITypeSymbol? symbol, Compilation compilation)
if (potentialSymbol is not IMethodSymbol getAwaiterMethod)
continue;

if (!compilation.IsSymbolAccessibleWithin(potentialSymbol, compilation.Assembly))
if (!_compilation.IsSymbolAccessibleWithin(potentialSymbol, _compilation.Assembly))
continue;

if (!getAwaiterMethod.Parameters.IsEmpty)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void AnalyzeSymbol(SymbolAnalysisContext context)
return;

var hasAsyncSuffix = method.Name.EndsWith("Async", StringComparison.Ordinal);
if (_awaitableTypes.IsAwaitable(method.ReturnType, context.Compilation))
if (_awaitableTypes.IsAwaitable(method.ReturnType))
{
if (!hasAsyncSuffix)
{
Expand Down Expand Up @@ -119,7 +119,7 @@ public void AnalyzeLocalFunction(OperationAnalysisContext context)
var method = operation.Symbol;

var hasAsyncSuffix = method.Name.EndsWith("Async", StringComparison.Ordinal);
if (_awaitableTypes.IsAwaitable(method.ReturnType, context.Compilation))
if (_awaitableTypes.IsAwaitable(method.ReturnType))
{
if (!hasAsyncSuffix)
{
Expand Down
19 changes: 14 additions & 5 deletions src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
Expand Down Expand Up @@ -77,6 +77,8 @@ private sealed class AnalyzerContext(Compilation compilation)
public INamedTypeSymbol? ComparerStringType { get; } = GetIComparerString(compilation);
public INamedTypeSymbol? EnumerableType { get; } = compilation.GetBestTypeByMetadataName("System.Linq.Enumerable");
public INamedTypeSymbol? ISetType { get; } = compilation.GetBestTypeByMetadataName("System.Collections.Generic.ISet`1")?.Construct(compilation.GetSpecialType(SpecialType.System_String));
public INamedTypeSymbol? IReadOnlySetType { get; } = compilation.GetBestTypeByMetadataName("System.Collections.Generic.IReadOnlySet`1")?.Construct(compilation.GetSpecialType(SpecialType.System_String));
public INamedTypeSymbol? IImmutableSetType { get; } = compilation.GetBestTypeByMetadataName("System.Collections.Immutable.IImmutableSet`1")?.Construct(compilation.GetSpecialType(SpecialType.System_String));

public void AnalyzeConstructor(OperationAnalysisContext ctx)
{
Expand Down Expand Up @@ -109,11 +111,18 @@ public void AnalyzeInvocation(OperationAnalysisContext ctx)
// Most ISet implementation already configured the IEqualityComparer in this constructor,
// so it should be ok to skip method calls on those types.
// A concrete use-case is HashSet<string>.Contains which has an extension method IEnumerable.Contains(value, comparer)
if (ISetType is not null && method.ContainingType.IsOrImplements(ISetType))
return;
foreach (var type in (ReadOnlySpan<ITypeSymbol?>)[ISetType, IReadOnlySetType, IImmutableSetType])
{

if (operation.Instance is not null && operation.Instance.GetActualType()?.IsOrImplements(ISetType) == true)
return;
if (type is null)
continue;

if (method.ContainingType.IsOrImplements(type))
return;

if (operation.Instance is not null && operation.Instance.GetActualType()?.IsOrImplements(type) is true)
return;
}

if (operation.IsImplicit && IsQueryOperator(operation) && ctx.Options.GetConfigurationValue(operation, Rule.Id + ".exclude_query_operator_syntaxes", defaultValue: false))
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
Expand Down Expand Up @@ -219,10 +219,10 @@ private Task<Project> CreateProject()
break;
}

AddNuGetReference("System.Collections.Immutable", "1.5.0", "lib/netstandard2.0/");

if (TargetFramework is not TargetFramework.Net7_0 and not TargetFramework.Net8_0 and not TargetFramework.Net9_0)
{
AddNuGetReference("System.Collections.Immutable", "1.5.0", "lib/netstandard2.0/");
AddNuGetReference("System.Numerics.Vectors", "4.5.0", "ref/netstandard2.0/");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using Meziantou.Analyzer.Rules;
using Meziantou.Analyzer.Test.Helpers;
using TestHelper;
Expand Down Expand Up @@ -458,6 +458,48 @@ await CreateProjectBuilder()
.ValidateAsync();
}

[Fact]
public async Task IReadOnlySet_Contain()
{
const string SourceCode = """
using System.Linq;
class TypeName
{
public void Test()
{
System.Collections.Generic.IReadOnlySet<string> obj = null;
obj.Contains("");
}
}
""";

await CreateProjectBuilder()
.WithTargetFramework(TargetFramework.Net6_0)
.WithSourceCode(SourceCode)
.ValidateAsync();
}

[Fact]
public async Task IImmutableSet_Contain()
{
const string SourceCode = """
using System.Linq;
class TypeName
{
public void Test()
{
System.Collections.Immutable.IImmutableSet<string> obj = null;
obj.Contains("");
}
}
""";

await CreateProjectBuilder()
.WithTargetFramework(TargetFramework.Net9_0)
.WithSourceCode(SourceCode)
.ValidateAsync();
}

[Fact]
public async Task StringArray_QuerySyntax_GroupBy_NoConfiguration()
{
Expand Down

0 comments on commit 0ad62c6

Please sign in to comment.