查找使用某些特殊属性

时间:2017-05-29 12:53:40

标签: c# roslyn roslyn-code-analysis

我想制作一个分析器,它会为代码中某些属性成员的每次出现抛出一条消息(severity = info)。这样可以模仿[Obsolete(...)]的行为,但只会抛出一条消息。

属性定义类似于

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

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

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

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

我的出发点是一个空的DiagnosticAnalyzer类:

[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我该如何继续前进?我需要在ordner中实现的逻辑是什么,以找到以明显方式归属的所有符号引用?

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

修改

根据@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 个答案:

答案 0 :(得分:1)

以下是我要采取的路线:

  1. 使用SemanticModelAction

  2. 注册context.RegisterSemanticModelAction
  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而不是{ {1}}正如我们之前所做的那样。)

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

  6. 修改

    关于您的修改,原因是您使用的是FullSpan

      

    此节点在字符中的绝对跨度,包括其前导和尾随琐事。

    使用Span或使用invocation.GetLocation()而忘记完全创建ReportDiagnostic对象。

    Roslyn reference非常彻底,所以它通常是个好看的地方。不要忘记Syntax Visualizer,这是另一种可以让你的生活更轻松100倍的工具。