使用 [ReadOnly] 属性标记参数并让分析器断言方法主体中没有重新分配 [英] Mark parameters with a [ReadOnly] attribute and let an analyzer assert, that no reassignment is in the method body

查看:61
本文介绍了使用 [ReadOnly] 属性标记参数并让分析器断言方法主体中没有重新分配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

无法将方法的参数标记为只读,因此无法在方法中重新分配,我开始考虑为此创建一个分析器.该参数将被归因于

Not being able to mark a parameter of a method as readonly so that it is impossible to be reassigned in the method, I started thinking abount creating an analyzer for this. The parameter would be attributed with

[AttributeUsage(AttributeTargets.Parameter)]
public class ReadOnlyAttribute: Attribute
{
  // ...
}

一个方法的签名是

public class Foo
{
  public void Bar([ReadOnly] MyObject o)
  {
    o.DoSomething()    // this is OK
    o = new MyObject() // this would be flagged by the analyzer
  }
}

到目前为止,我拥有的是包含这些成员的 DiagnosticAnalyzer 类:

What I have so far is an DiagnosticAnalyzer class with these members:

[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)
  {
    context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
  }

  private static void AnalyzeSymbol(SymbolAnalysisContext context)
  {
    var methodSymbol = context.Symbol as IMethodSymbol;
    var parameters = methodSymbol.Parameters;

    foreach (var p in parameters)
    {
      var attr = p.GetAttributes();

      foreach (var a in attr)
      {
        if (a.AttributeClass.Name == "ReadOnlyAttribute")
        {
          // now I have to find all references of p in order to check if any of them is a assignment
        }
      }
    }
  }
}

如何在方法体中找到一个参数的所有引用?我怎么知道其中哪一项是分配?

How do I find all reference of a parameter within the method body? And how do I know which one of them are assigments?

推荐答案

如果当前 IMethodSymbol 具有ReadOnlyAttribute"参数,我建议您为当前的 IMethodSymbol 获取 SyntaxNode,然后从 SyntaxNode 获取所有属于 AssignmentExpressionSyntax 的后代节点.因此,您只需要比较语句的标识符和您的参数.如果您有任何问题,请告诉我.

I sugges you get SyntaxNode for current IMethodSymbol if it has "ReadOnlyAttribute" parameter then get all descendant nodes from SyntaxNode that are AssignmentExpressionSyntax. So, you only need to compare statement' identifiers and your parameter. Give me know if you have some questions.

...
var methodSymbol = context.Symbol as IMethodSymbol;;
var parameters = methodSymbol.Parameters;
if (!parameters.SelectMany(p => p.GetAttributes()).Any(s => s.AttributeClass.Name == "ReadOnlyAttribute"))
{
    // So you don't have params with ReadOnly attribute
    return;
}

// So you have params with ReadOnly attribute
var location = methodSymbol.Locations.FirstOrDefault();
if (location == null)
{
    // throw or return 
}

// Can cahce CompilationRoot
var methodNode = location.SourceTree.GetCompilationUnitRoot().FindNode(locati‌​on.SourceSpan);
var childNodes = methodNode.ChildNodes().ToList();
// Expression-bodied memeber
var blockNode = childNodes.FirstOrDefault(s => s is BlockSyntax) ?? childNodes.FirstOrDefault(s => s is ArrowExpressionClauseSyntax);
if (blockNode == null)
{
    // throw or return 
}

// You can convert this linq to foreach and improve performance removing a where functions 
var assignmentIndetifiers = blockNode.DescendantNodes()
    .Select(s => s as AssignmentExpressionSyntax)
    .Where(s => s != null)
    .Select(s => s.Left as IdentifierNameSyntax)
    .Where(s => s != null)
    .Select(s => s.Identifier)
    .ToList();

// You retrive all left identifiers in assignment expressions from current block syntax and its descendant nodes
// You only need to check if assignmentIdentifiers contains your readonly parameter
// For example, you can compare by name (you can use custom equality comparer)
var names = assignmentIndetifiers.ToLookup(s => s.ValueText, EqualityComparer<string>.Default);
foreach (var parameter in parameters)
{
    if (names.Contains(parameter.Name))
    {
        foreach (var token in names[parameter.Name])
        {
            // throw that readonly argument is a assignment
            context.ReportDiagnostic(Diagnostic.Create(rule, token.GetLocation()));
        }
    }
}

这篇关于使用 [ReadOnly] 属性标记参数并让分析器断言方法主体中没有重新分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆