2017-05-29 71 views
1

我想制作一个分析器,该代码将为每个出现在代码中的某个特定成员发出一条消息(严重性=信息)。这模仿[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中实现哪些逻辑以找到所有以特殊方式归因的符号参考?

也许我完全在错误的轨道上,解决这个问题不应该通过分析仪来完成。还有哪些其他选项可用?

编辑

基于@陶的建议,我得到了它与下面的代码几乎工作:

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

可正常工作,但对此我报告的诊断是位置还不完全正确,因为它不仅是调用,而且还是尾随的空白。这是为什么?

+0

你有警告错误吗? –

回答

1

这是我会走的路线:

  1. 注册一个SemanticModelActioncontext.RegisterSemanticModelAction

  2. 查找的方法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可以从您注册的操作的上下文中获得。

  3. 通过所有InvocationExpression S(让他们以类似的方式,因为我们与methodDeclarations一样,加载其符号(请确保您使用GetSymbolInfo(invocation).Symbol这里,而不是GetDeclaredSymbol正如我们先前做的)。

  4. 比较如果调用符号在具有特殊属性的符号之中,则从步骤3到步骤2的符号以及ReportDiagnostic

编辑

关于你的编辑,那是因为你正在使用FullSpan

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

要么使用Span或使用invocation.GetLocation()和忘记共创建Location对象。

Roslyn reference是非常彻底的,所以它通常是一个很好的看看。另外别忘了Syntax Visualizer,这个工具可以让你的生活变得容易100倍。

+0

你让我走上了正轨。你的头顶建议是一尘不染。看到我的编辑有点后续问题。 – David

+0

@大卫我已经更新了关于你的编辑的答案 –