我正在写一个Rosyln分析仪/分析仪。它会检查以确保在访问类型上的另一个(潜在危险的)方法之前调用方法。为了表明我的意思,这里有一些我想要分析和失败的错误代码:
private void myMethod()
{
var myThing = new MyThing();
myThing.Value = null;
string value = myThing.GetValue(); // code blows up here as the internal value is null
}
这里的代码没问题,因为它调用的方法是否为null:
private void myMethod()
{
var myThing = new MyThing();
myThing.Value = null;
if(!myThing.HasValue)
{
return ;
}
string value = myThing.GetValue();
}
因此,它应该检查所有对GetValue
的来电是否都在调用HasValue
之前。
我刚刚开始使用Roslyn,所以可能比我最初(失败)的尝试更优雅:
1 - 声明我想检查调用表达式
context.RegisterSyntaxNodeAction(analyseMemberAccessNode, SyntaxKind.InvocationExpression);
2 - 在我的方法中,我得到方法名称(GetValue()
)
var expr = (InvocationExpressionSyntax)context.Node;
var memberAccess = expr.Expression as MemberAccessExpressionSyntax;
if (memberAccess?.Name.ToString() != "GetValue")
return;
3 - 然后我检查它是否是正确的'GetValue'
var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol as IMethodSymbol;
if (!memberSymbol?.OverriddenMethod.ToString().StartsWith("MyNamespace.MyThing.GetValue") ?? true)
return;
4 - 到此为止,一切都很好。所以我得到变量的名称
var e = memberAccess.Expression as IdentifierNameSyntax;
string variableName = e.Identifier.Text;
5 - 现在我被困了 - 我的理论是;获取包含方法,找到匹配variableName
的单个变量声明,找到它的用法,并确保在HasValue
之前调用GetValue
。
简而言之,使用Roslyn分析器(派生自DiagnosticAnalyzer
),如何确保在HasValue
之前调用GetValue
?
答案 0 :(得分:2)
您可能最好注册整个方法声明,而不是注册每个Invocation。然后,您可以跟踪所有MemberAccessExpressionSyntax
,并确保在HasValue
之前调用GetValue
的给定变量。为此,我会从MemberAccessExpressionSyntax
节点获取MethodDeclaration
个后代。
context.RegisterSyntaxNodeAction((analysisContext) =>
{
var invocations =
analysisContext.Node.DescendantNodes().OfType<MemberAccessExpressionSyntax>();
var hasValueCalls = new HashSet<string>();
foreach (var invocation in invocations)
{
var e = invocation.Expression as IdentifierNameSyntax;
if (e == null)
continue;
string variableName = e.Identifier.Text;
if (invocation.Name.ToString() == "HasValue")
{
hasValueCalls.Add(variableName);
}
if (invocation.Name.ToString() == "GetValue")
{
if (!hasValueCalls.Contains(variableName))
{
analysisContext.ReportDiagnostic(Diagnostic.Create(Rule, e.GetLocation()));
}
}
}
}, SyntaxKind.MethodDeclaration);