SyntaxNode.ContainsDiagnostics не работает с вашей собственной диагностикой? - PullRequest
0 голосов
/ 22 января 2019

Я пытаюсь написать анализатор, который запрещает пользователям предоставлять параметры, которые автоматически предоставляются (например, компилятором с [CallerMemberName]), и я хочу, чтобы анализатор выдавал ошибку при вводе параметра, который не должен бытьпри условии (чтобы указать параметр не должен быть предоставлен, я создал атрибут: DontProvideAttribute).

Дело в том, что автоматически предоставляемые параметры должны быть необязательными (в противном случае предоставленное пользователем значение будет записано поверх предоставленного автоматически), поэтому я провел второй анализ, чтобы запретить пользователям использовать [DontProvide] внеобязательные параметры.

И возникает проблема, я хочу, чтобы ошибка при вызове метода присутствовала только в том случае, если в объявлении параметра нет ошибки [DontProvide] should only be used on optional parameters, которая

foreach (SyntaxReference parameterDefinition in parameter.DeclaringSyntaxReferences)
{
    if (parameterDefinition.GetSyntax().ContainsDiagnostics)
    {
        return;
    }
}

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

То, что я пробовал:

- Изменение порядка диагностики для выполненияобъявление, которое анализируется перед вызовом метода

-Используйте .GetDiagnostics().Count() > 0 вместо

-Измените порядок текста в анализируемом документе, чтобы объявление метода было выше вызова метода

Анализатор:

public override void Initialize(AnalysisContext context)
{
    context.RegisterSymbolAction(AnalyzeParametersDeclaration, SymbolKind.Parameter);
    context.RegisterOperationAction(AnalyzeArguments, OperationKind.Argument);
}

private void AnalyzeArguments(OperationAnalysisContext context)
{
    IArgumentOperation reference = (IArgumentOperation)context.Operation;
    IParameterSymbol parameter = reference.Parameter;
    foreach (SyntaxReference parameterDefinition in parameter.DeclaringSyntaxReferences)
    {
        if (parameterDefinition.GetSyntax().ContainsDiagnostics)
            return;
    }
    foreach (AttributeData attribute in parameter.GetAttributes())
    {
        if (attribute.AttributeClass.Name == "DontProvideAttribute")
        {
            context.ReportDiagnostic(Diagnostic.Create(DontProvide, reference.Syntax.GetLocation(), parameter.Name));
        }
    }
}

private void AnalyzeParametersDeclaration(SymbolAnalysisContext context)
{
    IParameterSymbol parameter = (IParameterSymbol)context.Symbol;
    if (parameter.GetAttributes().Any(a => a.AttributeClass.Name == "DontProvideAttribute") && !parameter.IsOptional)
    {
        context.ReportDiagnostic(Diagnostic.Create(DontProvideOnlyForOptional, parameter.Locations[0]))         
    }
}

Некоторые тестовые коды для анализа:

using System;

namespace test
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            MyClass.MyMethod(null);
        }
    }

    internal class MyClass
    {
        public static void MyMethod([DontProvide] object parameter)
        {

        }
    }

    [AttributeUsage(AttributeTargets.Parameter)]
    public class DontProvideAttribute : Attribute
    {

    } 
}

PS : Компилятор может сказать вам, что context.RegisterSymbolAction(), используемый с SymbolKind.Parameter, не поддерживается, что неправильно ( подробнее здесь )

1 Ответ

0 голосов
/ 23 января 2019

Из обсуждения здесь и комментария @Kris Vandermotten

ContainsDiagnostics предназначен только для синтаксической диагностики (т. Е. Для диагностики внутри синтаксического дерева). не для диагностики, о которой сообщают более поздние проходы (т.е. семантическая диагностика или диагностика вашего собственного анализатора). И вот почему: конкретное синтаксическое дерево может содержаться во многих различных семантических контекстах из-за способности Рослина разбирать и рассуждать о вещах поэтому в одном контексте синтаксис может быть семантически правильным, а в другом - не будет как таковая, диагностика не хранится в самом дереве.

На самом деле решение в моем случае было довольно простым: мне просто нужно было удалить

foreach (SyntaxReference parameterDefinition in parameter.DeclaringSyntaxReferences)
{
    if (parameterDefinition.GetSyntax().ContainsDiagnostics)
        return;
}

и добавьте && parameter.IsOptionnal в оператор if:

foreach (AttributeData attribute in parameter.GetAttributes())
{
    if (attribute.AttributeClass.Name == "DontProvideAttribute")
    {
        context.ReportDiagnostic(Diagnostic.Create(DontProvide, reference.Syntax.GetLocation(), parameter.Name));
    }
}
...