A surgical Roslyn analyzer focused on CancellationToken propagation and honoring across public APIs, handlers, EF Core, HTTP calls, and Minimal APIs, with comprehensive code fixes.
$ dotnet add package CancelCop.AnalyzerA surgical Roslyn analyzer focused on CancellationToken propagation and honoring across public APIs, handlers, EF Core, HTTP calls, and Minimal APIs, with comprehensive automatic code fixes.
CancelCop helps you build responsive, cancellable .NET applications by ensuring CancellationToken is properly used throughout your codebase. It detects missing tokens and provides automatic fixes with a single click.
| Rule | Description | Severity |
|---|---|---|
| CC001 | Public async methods must have CancellationToken parameter | Warning |
| CC002 | CancellationToken must be propagated to async calls | Warning |
| CC003 | EF Core queries must pass CancellationToken | Warning |
| CC004 | HttpClient methods must pass CancellationToken | Warning |
| CC005A | MediatR handlers must accept CancellationToken | Warning |
| CC005B | Controller actions must accept CancellationToken | Warning |
| CC005C | Minimal API endpoints must accept CancellationToken | Warning |
| CC006 | CancellationToken should be the last parameter | Info |
dotnet add package CancelCop.Analyzer
Once installed, the analyzer runs automatically during build. It will:
// ❌ Before (CC001 warning)
public async Task ProcessDataAsync()
{
await Task.Delay(100);
}
// ✅ After (automatic fix applied)
public async Task ProcessDataAsync(CancellationToken cancellationToken = default)
{
await Task.Delay(100, cancellationToken);
}
// Detects missing tokens in:
await _context.Users.ToListAsync(cancellationToken);
await _context.Users.FirstOrDefaultAsync(u => u.Id == id, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
// Detects missing tokens in:
await httpClient.GetAsync(url, cancellationToken);
await httpClient.PostAsync(url, content, cancellationToken);
await httpClient.SendAsync(request, cancellationToken);
[HttpGet]
public async Task<IActionResult> GetUsers(CancellationToken cancellationToken)
{
var users = await _service.GetUsersAsync(cancellationToken);
return Ok(users);
}
app.MapGet("/users", async (CancellationToken ct) =>
await GetUsersAsync(ct));
public class MyHandler : IRequestHandler<MyRequest, MyResponse>
{
public async Task<MyResponse> Handle(
MyRequest request,
CancellationToken cancellationToken)
{
// ...
}
}
All rules are enabled by default with appropriate severity levels. You can configure them in .editorconfig:
[*.cs]
# Adjust severity (none, suggestion, warning, error)
dotnet_diagnostic.CC001.severity = warning
dotnet_diagnostic.CC006.severity = suggestion
Built with ❤️ using Roslyn and following TDD best practices.