Анализатор Roslyn: аннотации, допускающие значение NULL, в Linq

Я новичок в Roslyn, и мне нужно написать анализатор, который поможет улучшить аннотации, допускающие значение NULL, при использовании Linq. Цель состоит в том, чтобы заставить любое использование .XYZOrDefault () сохраняться в переменной / свойстве / методу, допускающем значение NULL. Например: если мы используем «var x = SomeKindOfList.FirstOrDefault ()», «x» необходимо пометить как допускающее значение NULL. Мы будем использовать C # 8, поэтому я имею в виду NRT. Идея достижения этого заключается в следующем:

  1. Определение того, используются ли универсальные типы в объявлении переменной или метода
  2. Используйте семантическую модель, чтобы определить, является ли левая часть типа выражения типом, допускающим значение NULL.
  3. Если левая часть выражения не является типом, допускающим значение NULL, произведите диагностику

Я зашел так далеко со своим кодированием:

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);
                    }
                }
            }
        }

Я так много читал о том, что вы можете сделать, что на данном этапе это информационная перегрузка. Мы будем очень признательны за любые статьи, фрагменты кода или советы, которые могут дать мне еще какое-то направление!


person BabyDoll    schedule 07.04.2020    source источник
comment
Запутались в цели. FirstOrDefault() возвращает ненулевое значение для таких типов значений, как bool или int. Если я не ошибаюсь, он возвращает только null для ссылочных типов. Вы имеете в виду ссылочные типы, допускающие значение NULL?   -  person Zer0    schedule 07.04.2020
comment
Предполагая, что вы говорите о NRT (это не ясно): обратите внимание, что это будет работать в .NET 5 - FirstOrDefault будет правильно аннотирован. Вы можете использовать ReferenceAssemblyAnnotator до тех пор, чтобы получить новейшие аннотации для BCL.   -  person canton7    schedule 07.04.2020
comment
Может быть, вы могли бы добавить код, который подхватит ваш анализатор? Неясно, имеете ли вы в виду NRT (вы пометили его как C # 8) или Nullable<T>   -  person canton7    schedule 07.04.2020
comment
Мои извинения, да, я имею в виду NRT. Я обновлю вопрос, чтобы прояснить это.   -  person BabyDoll    schedule 07.04.2020
comment
Ваш код зависит от System.Nullable1`, что не имеет ничего общего с NRT. Вот что меня смутило.   -  person canton7    schedule 07.04.2020
comment
Это моя первая попытка сделать это, поэтому я прошу прощения, если мое намерение в фрагменте кода неясно. Я просто не понимаю, где могла бы быть хорошая отправная точка.   -  person BabyDoll    schedule 07.04.2020
comment
Я думаю, что правильнее всего будет признать, что эта функция уже встроена в компилятор (при условии, что вызов FirstOrDefault аннотирован правильно: если вы используете что-то до .NET 5, используйте ReferenceAssemblyAnnotator, о котором я упоминал ранее.   -  person canton7    schedule 07.04.2020
comment
Есть ли другой анализатор, который вы можете порекомендовать попробовать? Я искал на GitHub альтернативы, пока жду отзывов о ReferenceAssemblyAnnotator   -  person BabyDoll    schedule 08.04.2020


Ответы (1)


В конце концов, мы написали наш собственный анализатор в качестве временного решения, пока мы не сможем перейти на .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);
                        }
                    }
                }
            }
        }
    }

Надеюсь, это поможет кому-то другому, который, возможно, просто ищет хорошую отправную точку при разработке чего-то подобного.

person BabyDoll    schedule 15.04.2020