Roslyn analyzers help to write high quality code in .NET using the Roslyn API and C#. The are like an eslinter in the node.js world.

First of all. This is an analyzer, not a code-fix.

1) Lets create a CancellationTokenAnalyzer.csproj with the following content. (netstandard2.0 is required. We can still use some new C# features by specifying the language version)

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <LangVersion>preview</LangVersion>
    <Nullable>enable</Nullable>
    <WarningsAsErrors>Nullable</WarningsAsErrors>
    👇 Required for debugging in Visual Studio
    <IsRoslynComponent>true</IsRoslynComponent>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
  </ItemGroup>

</Project>

2) Create a file called CancellationTokenArgumentAnalyzer.cs

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CancellationTokenArgumentAnalyzer : DiagnosticAnalyzer
{
    private static readonly DiagnosticDescriptor _rule = new(
        id: "CancellationTokenArgumentAnalyzer",
        title: "Checks missing CancellationToken argument",
        messageFormat: "Missing CancellationToken argument for '{0}'.",
        category: "CancellationToken",
        defaultSeverity: DiagnosticSeverity.Warning,
        isEnabledByDefault: true,
        description: "Checks missing CancellationToken argument.");

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(_rule);

    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();

        context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation);
    }

    private static void AnalyzeOperation(OperationAnalysisContext context)
    {
        if (context.Operation is not IInvocationOperation invocationOperation)
        {
            return;
        }

        var methodSymbol = invocationOperation.TargetMethod;
        var arguments = invocationOperation.Arguments;

        if (methodSymbol.ReturnType.Name is not "Task" or "ValueTask")
        {
            return;
        }

        if (!arguments.Any(x => x.Value.Type?.Name == "CancellationToken"))
        {
            var diagnostic = Diagnostic.Create(_rule, invocationOperation.Syntax.GetLocation(), methodSymbol.Name);
            context.ReportDiagnostic(diagnostic);
        }
    }
}

3) Add the following line to the consuming MyApplication.csproj.

<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    👇
    <PackageReference Include="..\CancellationTokenAnalyzer\CancellationTokenAnalyzer.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
  </ItemGroup>

</Project>

4) Compile the consuming MyApplication.csproj

dotnet build

Done! You should now see all warnings about missing CancellationToken's. 😊

Visual Studio (not VS Code) will run analyzers automatically while coding. Unfortunately performance is a huge issue. So we should write analyzers as efficient as possible.