查找具有某些特殊属性的符号的所有引用

Find all references of a symbol that is attributed with some special attribute

我想制作一个分析器,它会针对代码中某个属性成员的每次出现抛出一条消息(严重性 = 信息)。这模仿了 [Obsolete(...)] 的行为,但只抛出一条消息。

属性定义类似于

public class ThrowsMessageAttribute : Attribute
{
  // ...
}

我要为其发送消息的成员将被归因于它:

public class Foo
{
  [ThrowsMessage]
  public void Bar() { }
}

对于我在代码中使用的每个 Bar(),我现在都会在错误列表的消息选项卡中获得一个条目。

我的起点是一个空的 DiagnosticAnalyzer class:

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class MyDiagnosticAnalyzer : DiagnosticAnalyzer
{
  private static readonly DiagnosticDescriptor Descriptor =
    new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Info, true, Description, HelpLink);

  public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Descriptor);

  public override void Initialize(AnalysisContext context)
  {
    // how to go on from here?
  }
}

有了 AnalysisContext 我该如何继续前进?我需要实现什么逻辑才能找到所有以不同方式归因的符号引用?

也许我完全走错了路,解决这个问题不应该通过分析器来完成。还有哪些其他选项可用?

编辑

根据@Tamás 的建议,我使用以下代码几乎可以正常工作:

public override void Initialize(AnalysisContext context)
{
  context.RegisterSemanticModelAction(Analyze);
}

private static void Analyze(SemanticModelAnalysisContext context)
{
  var semanticModel = context.SemanticModel;
  var step2 = GetSymbolsOfAttributedMethods(semanticModel, "ThrowsMessage");
  Step3(context, list2, semanticModel);
}

private static List<ISymbol> GetSymbolsOfAttributedMethods(SemanticModel semanticModel, string attributeName)
{
  var methodDeclarations = semanticModel.SyntaxTree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>();
  var symbolList = new List<ISymbol>();

  foreach (var declaration in methodDeclarations)
  {
    foreach (var attributeList in declaration.AttributeLists)
    {
      if (attributeList.Attributes.Any(a => (a.Name as IdentifierNameSyntax)?.Identifier.Text == attributeName))
      {
        symbolList.Add(semanticModel.GetDeclaredSymbol(declaration));
        break;
      }
    }
  }
  return symbolList;
}

private static void Step3(SemanticModelAnalysisContext context, List<ISymbol> attributedSymbols, SemanticModel semanticModel)
{
  var invocationExpressions = semanticModel.SyntaxTree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>();

  foreach (var invocation in invocationExpressions)
  {
    var symbol = semanticModel.GetSymbolInfo(invocation).Symbol;

    if (attributedSymbols.Contains(symbol))
    {
      var l = Location.Create(context.SemanticModel.SyntaxTree, invocation.FullSpan);
      context.ReportDiagnostic(Diagnostic.Create(Rule, l));
    }
  }
}

这按预期工作,但我报告诊断的位置还不太正确,因为它不仅是调用而且是尾随空格。这是为什么?

这是我要走的路线:

  1. context.RegisterSemanticModelAction

  2. 注册一个SemanticModelAction
  3. 找到 MethodDeclaration 个具有您的特殊属性的方法并获取该方法的符号。这看起来像这样:

    private List<ISymbol> GetSymbolsOfAttributedMethods(string attributeName)
    {
        var methodDeclarations = semanticModel.SyntaxTree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>();
        var symbolList = new List<ISymbol>();
    
        foreach (var declaration in methodDeclarations)
        {
            foreach (var attributeList in declaration.AttributeLists)
            {
                if (attributeList.Attributes.Any(a => (a.Name as IdentifierNameSyntax)?.Identifier.Text == attributeName))
                {
                    symbolList.Add(semanticModel.GetDeclaredSymbol(declaration));
                    break;
                }
            }
        }
        return symbolList;
    }
    

    semanticModel 可以从您注册的操作的上下文中获取。

  4. 遍历所有 InvocationExpression(以与 methodDeclarations 类似的方式获取它们),加载它们的符号(确保使用 GetSymbolInfo(invocation).Symbol这里而不是我们之前做的GetDeclaredSymbol

  5. 将步骤 3 中的符号与步骤 2 中的符号进行比较,如果调用的符号属于具有特殊属性的符号,则 ReportDiagnostic

编辑

关于您的编辑,是因为您使用的是FullSpan

The absolute span of this node in characters, including its leading and trailing trivia.

要么使用 Span or use invocation.GetLocation() 而完全忘记创建 Location 对象。

Roslyn reference is pretty thorough so it's usually a good place to look. And don't forget the Syntax Visualizer,另一个可以让您的生活轻松 100 倍的工具。