Roslyn 分析器:Linq 中的可空注释

Roslyn analyser: Nullable annotations in Linq

我是 Roslyn 的新手,我必须编写一个分析器来帮助改进使用 Linq 时可为空的注释。目标是强制将 .XYZOrDefault() 的任何用法存储在可空类型 variable/property/method 签名中。例如:如果我们使用 "var x = SomeKindOfList.FirstOrDefault()","x" 需要标记为可空。我们将使用 C# 8,所以我指的是 NRT。实现这一目标的想法是:

  1. 正在确定泛型类型是否在变量中使用或 方法声明
  2. 使用语义模型判断是否是左手 表达式类型的一侧是可空类型
  3. 如果表达式的左侧不是可空类型,则生成 诊断

我的编码已经走到这一步了:

private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
    {
        Compilation compilation = context.Compilation;
        var localDeclarations = context.Node.DescendantNodes().OfType<VariableDeclarationSyntax>(); 
        foreach (var declaration in localDeclarations)
        {
            // Find implicitly typed variable declarations.
            if (declaration.Type.IsVar)
            {
                foreach (var variable in declaration.Variables)
                {
                    var variableSymbol = ((ILocalSymbol)context.SemanticModel.GetDeclaredSymbol(variable)).Type;
                    var invocationSymbol = context.SemanticModel.GetOperation(variable.Initializer.Value).Type;

                    if (!((INamedTypeSymbol)variableSymbol).IsGenericType || declaration.Type.IsVar)
                    {
                        // For all such symbols, produce a diagnostic.
                        var diagnostic = Diagnostic.Create(Rule, variableSymbol.Locations[0], variableSymbol.Name);

                        context.ReportDiagnostic(diagnostic);
                    }
                }
            }
        }

我已经阅读了很多关于您在现阶段可以做什么的信息,这是一个信息过载。任何可以给我更多指导的文章、代码片段或建议都将不胜感激!

最后,我们最终编写了自己的分析器作为临时解决方案,直到我们可以迁移到 .Net 5。我们想出的最基本的实现是以下代码:

    private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
    {
        var localDeclarations = context.Node.DescendantNodes().OfType<VariableDeclarationSyntax>();

        foreach (var declaration in localDeclarations)
        {                
            foreach (var variable in declaration.Variables)
            {
                ITypeSymbol? variableSymbol = ((ILocalSymbol)context.SemanticModel.GetDeclaredSymbol(variable)).Type; 
                IOperation? invocationOperationInfo = context.SemanticModel.GetOperation(variable.Initializer.Value);

                if (invocationOperationInfo is IInvocationOperation op)
                {
                    if (op.TargetMethod.IsGenericMethod)
                    {
                        if (!((INamedTypeSymbol)variableSymbol).IsGenericType || declaration.Type.IsVar)
                        {
                            // For all such symbols, produce a diagnostic.
                            var diagnostic = Diagnostic.Create(Rule, variable.GetLocation());

                            context.ReportDiagnostic(diagnostic);
                        }
                    }
                }
            }
        }
    }

希望这可以帮助其他人,他们可能只是在开发类似的东西时寻找一个好的起点。