Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public Context(Compilation compilation)
ValueTaskAwaiterOfTSymbol = compilation.GetBestTypeByMetadataName("System.Runtime.CompilerServices.ValueTaskAwaiter`1");

ThreadSymbol = compilation.GetBestTypeByMetadataName("System.Threading.Thread");
SemaphoreSlimSymbol = compilation.GetBestTypeByMetadataName("System.Threading.SemaphoreSlim");
TimeSpanSymbol = compilation.GetBestTypeByMetadataName("System.TimeSpan");

DbContextSymbol = compilation.GetBestTypeByMetadataName("Microsoft.EntityFrameworkCore.DbContext");
DbSetSymbol = compilation.GetBestTypeByMetadataName("Microsoft.EntityFrameworkCore.DbSet`1");
Expand Down Expand Up @@ -130,6 +132,8 @@ public Context(Compilation compilation)
private INamedTypeSymbol? ValueTaskAwaiterOfTSymbol { get; }

private INamedTypeSymbol? ThreadSymbol { get; }
private INamedTypeSymbol? SemaphoreSlimSymbol { get; }
private INamedTypeSymbol? TimeSpanSymbol { get; }

private INamedTypeSymbol? DbContextSymbol { get; }
private INamedTypeSymbol? DbSetSymbol { get; }
Expand Down Expand Up @@ -240,6 +244,12 @@ private bool HasAsyncEquivalent(IInvocationOperation operation, [NotNullWhen(tru
return false;
}

// SemaphoreSlim.Wait(0) is a non-blocking try-acquire pattern, skip it
else if (SemaphoreSlimSymbol is not null && targetMethod.Name == "Wait" && targetMethod.ContainingType.IsEqualTo(SemaphoreSlimSymbol) && IsSemaphoreSlimWaitWithZeroTimeout(operation))
{
return false;
}

// Search async equivalent: sample.Write() => sample.WriteAsync()
if (!targetMethod.ReturnType.OriginalDefinition.IsEqualToAny(TaskSymbol, TaskOfTSymbol))
{
Expand Down Expand Up @@ -305,6 +315,28 @@ private bool IsPotentialMember(IInvocationOperation operation, IMethodSymbol met
return false;
}

private bool IsSemaphoreSlimWaitWithZeroTimeout(IInvocationOperation operation)
{
if (operation.Arguments.Length == 0)
return false;

var firstArgument = operation.Arguments[0];
var constantValue = firstArgument.Value.ConstantValue;

// Check for Wait(0) - integer literal 0
if (constantValue.HasValue && constantValue.Value is int intValue && intValue == 0)
return true;

// Check for Wait(TimeSpan.Zero)
if (TimeSpanSymbol is not null && firstArgument.Value is IMemberReferenceOperation memberRef)
{
if (memberRef.Member.Name == nameof(TimeSpan.Zero) && memberRef.Member.ContainingType.IsEqualTo(TimeSpanSymbol))
return true;
}

return false;
}

internal void AnalyzePropertyReference(OperationAnalysisContext context)
{
var operation = (IPropertyReferenceOperation)context.Operation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1014,4 +1014,81 @@ await CreateProjectBuilder()
""")
.ValidateAsync();
}

[Fact]
public async Task SemaphoreSlim_Wait_NoDiagnostic()
{
await CreateProjectBuilder()
.WithSourceCode("""
using System.Threading;
using System.Threading.Tasks;
class Test
{
public async Task A()
{
var semaphore = new SemaphoreSlim(1);
semaphore.Wait(0);
}
}
""")
.ValidateAsync();
}

[Fact]
public async Task SemaphoreSlim_Wait_TimeSpanZero_NoDiagnostic()
{
await CreateProjectBuilder()
.WithSourceCode("""
using System;
using System.Threading;
using System.Threading.Tasks;
class Test
{
public async Task A()
{
var semaphore = new SemaphoreSlim(1);
semaphore.Wait(TimeSpan.Zero);
}
}
""")
.ValidateAsync();
}

[Fact]
public async Task SemaphoreSlim_Wait_NonZero_Diagnostic()
{
await CreateProjectBuilder()
.WithSourceCode("""
using System.Threading;
using System.Threading.Tasks;
class Test
{
public async Task A()
{
var semaphore = new SemaphoreSlim(1);
[||]semaphore.Wait(100);
}
}
""")
.ValidateAsync();
}

[Fact]
public async Task SemaphoreSlim_Wait_NoArgs_Diagnostic()
{
await CreateProjectBuilder()
.WithSourceCode("""
using System.Threading;
using System.Threading.Tasks;
class Test
{
public async Task A()
{
var semaphore = new SemaphoreSlim(1);
[||]semaphore.Wait();
}
}
""")
.ValidateAsync();
}
}
Loading