如何使用Roslyn查找变量的先前用法? [英] How can I find previous usages of a variable using Roslyn?
问题描述
我正在写一个Rosyln分析器/分析器.它检查以确保在访问类型上的另一个(潜在危险)方法之前已调用一个方法.为了说明我的意思,下面是一些我想分析并失败的错误代码:
private void myMethod(){var myThing = new MyThing();myThing.Value = null;字符串值= myThing.GetValue();//由于内部值为null,因此代码在此处爆炸}
这是可以的代码,因为它调用了一个说明是否为空的方法:
private void myMethod(){var myThing = new MyThing();myThing.Value = null;if(!myThing.HasValue){返回 ;}字符串值= myThing.GetValue();}
因此,它应该检查对 GetValue
的所有调用是否先于对 HasValue
的调用.
我刚开始使用罗斯林(Roslyn),所以可能有一种比我最初的尝试(失败)尝试更优雅的方法:
1-声明我要检查调用表达式
context.RegisterSyntaxNodeAction(analyseMemberAccessNode,SyntaxKind.InvocationExpression);
2-在我的方法中,我获得了方法名称( GetValue()
)
var expr =(InvocationExpressionSyntax)context.Node;var memberAccess = expr.Expression作为MemberAccessExpressionSyntax;如果(memberAccess?.Name.ToString()!="GetValue")返回;
3-然后检查是否正确的"GetValue"
var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol作为IMethodSymbol;如果(!memberSymbol?.OverriddenMethod.ToString().StartsWith("MyNamespace.MyThing.GetValue")??是)返回;
4-到这里,一切都很好.所以我得到了变量
的名称 var e = memberAccess.Expression作为IdentifierNameSyntax;字符串variableName = e.Identifier.Text;
5-现在我被困住了-我的理论是:获取包含方法,找到与 variableName
匹配的单个变量声明,查找其用法,并确保在 GetValue
之前调用 HasValue
./p>
简而言之,使用Roslyn分析器(源自 DiagnosticAnalyzer
),如何确保在 GetValue
之前调用 HasValue
?>
最好为整个方法声明注册,而不是为每个调用注册.然后,您可以跟踪所有 MemberAccessExpressionSyntax
,并确保对于给定的变量,在 GetValue
之前调用 HasValue
.为此,我将从 MethodDeclaration
节点获取 MemberAccessExpressionSyntax
后代.
context.RegisterSyntaxNodeAction((analysisContext)=>{var调用=analysisContext.Node.DescendantNodes().OfType< MemberAccessExpressionSyntax>();var hasValueCalls = new HashSet< string>();foreach(调用中的var调用){var e = invocation.Expression as IdentifierNameSyntax;如果(e == null)继续;字符串variableName = e.Identifier.Text;如果(invocation.Name.ToString()=="HasValue"){hasValueCalls.Add(variableName);}如果(invocation.Name.ToString()=="GetValue"){如果(!hasValueCalls.Contains(variableName)){analysisContext.ReportDiagnostic(Diagnostic.Create(Rule,e.GetLocation()));}}}},SyntaxKind.MethodDeclaration);
I'm writing a Rosyln analyser/analyzer. It checks to ensure that a method is called before accessing another (potentially dangerous) method on a type. To show what I mean, here's some bad code that I want to analyse and fail on:
private void myMethod()
{
var myThing = new MyThing();
myThing.Value = null;
string value = myThing.GetValue(); // code blows up here as the internal value is null
}
Here's code that's OK because it calls a method that says whether it's null:
private void myMethod()
{
var myThing = new MyThing();
myThing.Value = null;
if(!myThing.HasValue)
{
return ;
}
string value = myThing.GetValue();
}
So, it should check that all calls to GetValue
are preceeded by a call to HasValue
.
I've just started with Roslyn, so there's probably a more elegant way than my initial (failing) attempt at:
1 - Declare that I want to inspect invocation expressions
context.RegisterSyntaxNodeAction(analyseMemberAccessNode, SyntaxKind.InvocationExpression);
2 - In my method, I get the method name (GetValue()
)
var expr = (InvocationExpressionSyntax)context.Node;
var memberAccess = expr.Expression as MemberAccessExpressionSyntax;
if (memberAccess?.Name.ToString() != "GetValue")
return;
3 - I then check to see if it's the right 'GetValue'
var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol as IMethodSymbol;
if (!memberSymbol?.OverriddenMethod.ToString().StartsWith("MyNamespace.MyThing.GetValue") ?? true)
return;
4 - Up to here, everything is fine. So I get the name of the variable
var e = memberAccess.Expression as IdentifierNameSyntax;
string variableName = e.Identifier.Text;
5 - now I'm stuck - my theory was to; get the containing method, find the single variable declaration that matches variableName
, find usages of that, and ensure that HasValue
is called before GetValue
.
In short, using a Roslyn analyser (deriving from DiagnosticAnalyzer
), how do I ensure that HasValue
is called before GetValue
?
Instead of registering for each Invocation, you might be better off registering for the entire method declaration. Then you can keep track of all MemberAccessExpressionSyntax
and ensure that for a given variable that HasValue
is called before GetValue
. To do that, I would get the MemberAccessExpressionSyntax
descendants from the MethodDeclaration
node.
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);
这篇关于如何使用Roslyn查找变量的先前用法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!