Introduction

edit

Meziantou.Framework.Roslyn is a set of helpers for the projects that use the Roslyn APIs: analyzers, code fixes, suppressors and source generators. The package ships C# source files instead of an assembly: they are compiled into your project as internal types, so an analyzer has no extra assembly to load and no version conflict with another analyzer that uses the package.

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class AvoidTaskWaitAnalyzer : DiagnosticAnalyzer
{
    private static readonly DiagnosticDescriptor Rule = new(
        id: "SAMPLE001",
        title: "Do not call Task.Wait",
        messageFormat: "Do not call '{0}', await the task instead",
        category: "Usage",
        defaultSeverity: DiagnosticSeverity.Warning,
        isEnabledByDefault: true);

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [Rule];

    public override void Initialize(AnalysisContext context)
    {
        context.EnableConcurrentExecution();
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.RegisterCompilationStartAction(context =>
        {
            var taskType = context.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.Task");
            if (taskType is null)
                return;

            context.RegisterOperationAction(context =>
            {
                var operation = (IInvocationOperation)context.Operation;
                if (operation.TargetMethod.Name is "Wait" && operation.TargetMethod.ContainingType.IsOrInheritsFrom(taskType))
                {
                    // Underline "Wait" in "task.Wait()" instead of the whole invocation
                    context.ReportDiagnostic(Rule, operation, DiagnosticInvocationReportOptions.ReportOnMember, operation.TargetMethod.Name);
                }
            }, OperationKind.Invocation);
        });
    }
}

In this analyzer, GetBestTypeByMetadataName, IsOrInheritsFrom and the ReportDiagnostic overload come from the package.

What you can do with it

  • Query symbols and types: inheritance, interfaces, assignability, attributes, overrides, visibility... See Symbols and types.
  • Navigate operations and syntax: unwrap conversions, find the value of a local, parenthesize an expression. See Operations and syntax.
  • Recognize generated code as the analyzer driver does. See Generated code.
  • Report diagnostics on the right part of a declaration or an invocation, and filter them globally. See Reporting diagnostics.
  • Write code for several Roslyn versions with the ROSLYN_* and CSHARP* constants. See Build configuration.

Next steps