Roslyn - 获取对分配给 属性 的字符串的引用
Roslyn - obtain reference to a string assigned to property
在多个项目的 Visual Studio 解决方案中,我的代码片段类似于:
sqlCommand.CommandText = "Some SQL statement";
我能够通过以下方式获得对 CommandText
作为解决方案内部 Callee
的所有引用:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
// ...
var project = _solution.GetProject("my-project");
var compilation = await project.GetCompilationAsync();
var sqlCmdSymbol = compilation.GetTypeByMetadataName(typeof(SqlCommand).FullName);
var cmdTextSymbol = sqlCmdSymbol.GetMembers("CommandText").First();
IEnumerable<SymbolCallerInfo> allReferences = await SymbolFinder.FindCallersAsync(cmdTextSymbol, _solution);
var calledSymbolReferences = allReferences.Where(r => SymbolEqualityComparer.Default.Equals(r.CalledSymbol, cmdTextSymbol)).ToArray();
foreach (SymbolCallerInfo reference in calledSymbolReferences)
{
// How to get from `SymbolCallerInfo` to a `LiteralExpressionSyntax` following it?
// (eg .CommandText = "Some SQL statement";)
}
Microsoft Docs 中有一个代码示例如何捕获字符串文字:
// Use the syntax model to find the literal string:
LiteralExpressionSyntax helloWorldString = root.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.Single();
// Use the semantic model for type information:
TypeInfo literalInfo = model.GetTypeInfo(helloWorldString);
但我不确定如何从 SymbolCallerInfo
类型的引用到 属性 CommandText
到分配给它的字符串?
您需要寻找 AssignmentExpressionSyntax
的 CommandText
属性 语法作为其 Left
表达式。
您可以从 SymbolCallerInfo reference
的 Locations
属性 中找到该语法,但根据我的经验,反过来更容易。
您要查找分配给 属性 的文本,因此我会搜索 AssignmentExpressionSyntax
。
在分析器中,您将重写 Initialize
方法,如下所示:
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterSyntaxNodeAction(
AnalyzeAssignmentExpression, SyntaxKind.SimpleAssignmentExpression);
}
请注意,我们在这里只寻找简单的作业;即使用 =
的赋值。我想您也可以考虑寻找 SyntaxKind.AddAssignmentExpression
,即使用 +=
进行赋值,但要确定赋值的确切内容并不容易,因此我将其排除在外。
所以现在我们需要实现AnalyzeAssignmentExpression
方法。
private void AnalyzeAssignmentExpression(SyntaxNodeAnalysisContext context)
我们做的第一件事是获取我们正在分析的AssignmentExpressionSyntax node
:
private void AnalyzeAssignmentExpression(SyntaxNodeAnalysisContext context)
{
var node = (AssignmentExpressionSyntax)context.Node;
该节点具有我们需要查看的 Left
和 Right
属性。让我们从 Left
属性 开始,找出它指的是什么符号。
ISymbol left = context.SemanticModel.GetSymbolInfo(
node.Left, context.CancellationToken).Symbol;
我们需要查明这个 Symbol 是否确实是我们的 CommandText
属性.
这里有一个小的辅助方法可以做到这一点:
private static bool IsCommandText(ISymbol symbol)
=> symbol is IPropertySymbol
{
Name: "CommandText",
ContainingType:
{
Name: "SqlCommand",
ContainingNamespace:
{
Name: "SqlClient",
ContainingNamespace:
{
Name: "Data",
ContainingNamespace:
{
// Allow both Microsoft.Data.SqlClient and System.Data.SqlClient
ContainingNamespace:
{
IsGlobalNamespace: true
}
}
}
}
}
};
(如果您知道您正在寻找的确切类型,您也可以将它的 ContainingType
与您使用 Compilation.GetTypeByMetadataName()
查找过的符号进行比较。我没有,所以我使用这个图案。)
使用该辅助方法,我们可以测试我们的 left
交易品种。如果不是我们的CommandText
属性,我们可以停止分析这个node
.
if (!IsCommandText(left))
{
return;
}
现在让我们看一下 Right
属性,正在分配的语法。您说您正在寻找 LiteralExpressionSyntax
,但我建议您查看计算结果为常量字符串的任何语法。它不一定是文字。它可以是常量的名称,例如,两个文字的串联,或许多其他类型的表达式。但我认为你真正关心的是它的价值。那么让我们看看 Right
属性 是否有一个常量字符串值。 (我假设你对这里的值 null
不感兴趣。)如果它有一个常量字符串值,我们就找到了我们要找的东西。
var right = context.SemanticModel.GetConstantValue(node.Right, context.CancellationToken);
if (right.HasValue && right.Value is string value)
{
// value is a constant being assigned to SqlCommand.CommandText
// your further analysis goes here
}
}
整个方法:
private void AnalyzeAssignmentExpression(SyntaxNodeAnalysisContext context)
{
var node = (AssignmentExpressionSyntax)context.Node;
ISymbol left = context.SemanticModel.GetSymbolInfo(
node.Left, context.CancellationToken).Symbol;
if (!IsCommandText(left))
{
return;
}
var right = context.SemanticModel.GetConstantValue(node.Right, context.CancellationToken);
if (right.HasValue && right.Value is string value)
{
// value is a constant being assinged to SqlCommand.CommandText
// your further analysis goes here
}
}
如果您不是在分析器中执行此操作,您可以使用 root.DescendantNodes().OfType<AssignmentExpressionSyntax>()
找到 AssignmentExpressionSyntax
节点,根据您的问题,我认为您已经知道如何获得 [=45] =] 给他们。
在多个项目的 Visual Studio 解决方案中,我的代码片段类似于:
sqlCommand.CommandText = "Some SQL statement";
我能够通过以下方式获得对 CommandText
作为解决方案内部 Callee
的所有引用:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
// ...
var project = _solution.GetProject("my-project");
var compilation = await project.GetCompilationAsync();
var sqlCmdSymbol = compilation.GetTypeByMetadataName(typeof(SqlCommand).FullName);
var cmdTextSymbol = sqlCmdSymbol.GetMembers("CommandText").First();
IEnumerable<SymbolCallerInfo> allReferences = await SymbolFinder.FindCallersAsync(cmdTextSymbol, _solution);
var calledSymbolReferences = allReferences.Where(r => SymbolEqualityComparer.Default.Equals(r.CalledSymbol, cmdTextSymbol)).ToArray();
foreach (SymbolCallerInfo reference in calledSymbolReferences)
{
// How to get from `SymbolCallerInfo` to a `LiteralExpressionSyntax` following it?
// (eg .CommandText = "Some SQL statement";)
}
Microsoft Docs 中有一个代码示例如何捕获字符串文字:
// Use the syntax model to find the literal string:
LiteralExpressionSyntax helloWorldString = root.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.Single();
// Use the semantic model for type information:
TypeInfo literalInfo = model.GetTypeInfo(helloWorldString);
但我不确定如何从 SymbolCallerInfo
类型的引用到 属性 CommandText
到分配给它的字符串?
您需要寻找 AssignmentExpressionSyntax
的 CommandText
属性 语法作为其 Left
表达式。
您可以从 SymbolCallerInfo reference
的 Locations
属性 中找到该语法,但根据我的经验,反过来更容易。
您要查找分配给 属性 的文本,因此我会搜索 AssignmentExpressionSyntax
。
在分析器中,您将重写 Initialize
方法,如下所示:
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterSyntaxNodeAction(
AnalyzeAssignmentExpression, SyntaxKind.SimpleAssignmentExpression);
}
请注意,我们在这里只寻找简单的作业;即使用 =
的赋值。我想您也可以考虑寻找 SyntaxKind.AddAssignmentExpression
,即使用 +=
进行赋值,但要确定赋值的确切内容并不容易,因此我将其排除在外。
所以现在我们需要实现AnalyzeAssignmentExpression
方法。
private void AnalyzeAssignmentExpression(SyntaxNodeAnalysisContext context)
我们做的第一件事是获取我们正在分析的AssignmentExpressionSyntax node
:
private void AnalyzeAssignmentExpression(SyntaxNodeAnalysisContext context)
{
var node = (AssignmentExpressionSyntax)context.Node;
该节点具有我们需要查看的 Left
和 Right
属性。让我们从 Left
属性 开始,找出它指的是什么符号。
ISymbol left = context.SemanticModel.GetSymbolInfo(
node.Left, context.CancellationToken).Symbol;
我们需要查明这个 Symbol 是否确实是我们的 CommandText
属性.
这里有一个小的辅助方法可以做到这一点:
private static bool IsCommandText(ISymbol symbol)
=> symbol is IPropertySymbol
{
Name: "CommandText",
ContainingType:
{
Name: "SqlCommand",
ContainingNamespace:
{
Name: "SqlClient",
ContainingNamespace:
{
Name: "Data",
ContainingNamespace:
{
// Allow both Microsoft.Data.SqlClient and System.Data.SqlClient
ContainingNamespace:
{
IsGlobalNamespace: true
}
}
}
}
}
};
(如果您知道您正在寻找的确切类型,您也可以将它的 ContainingType
与您使用 Compilation.GetTypeByMetadataName()
查找过的符号进行比较。我没有,所以我使用这个图案。)
使用该辅助方法,我们可以测试我们的 left
交易品种。如果不是我们的CommandText
属性,我们可以停止分析这个node
.
if (!IsCommandText(left))
{
return;
}
现在让我们看一下 Right
属性,正在分配的语法。您说您正在寻找 LiteralExpressionSyntax
,但我建议您查看计算结果为常量字符串的任何语法。它不一定是文字。它可以是常量的名称,例如,两个文字的串联,或许多其他类型的表达式。但我认为你真正关心的是它的价值。那么让我们看看 Right
属性 是否有一个常量字符串值。 (我假设你对这里的值 null
不感兴趣。)如果它有一个常量字符串值,我们就找到了我们要找的东西。
var right = context.SemanticModel.GetConstantValue(node.Right, context.CancellationToken);
if (right.HasValue && right.Value is string value)
{
// value is a constant being assigned to SqlCommand.CommandText
// your further analysis goes here
}
}
整个方法:
private void AnalyzeAssignmentExpression(SyntaxNodeAnalysisContext context)
{
var node = (AssignmentExpressionSyntax)context.Node;
ISymbol left = context.SemanticModel.GetSymbolInfo(
node.Left, context.CancellationToken).Symbol;
if (!IsCommandText(left))
{
return;
}
var right = context.SemanticModel.GetConstantValue(node.Right, context.CancellationToken);
if (right.HasValue && right.Value is string value)
{
// value is a constant being assinged to SqlCommand.CommandText
// your further analysis goes here
}
}
如果您不是在分析器中执行此操作,您可以使用 root.DescendantNodes().OfType<AssignmentExpressionSyntax>()
找到 AssignmentExpressionSyntax
节点,根据您的问题,我认为您已经知道如何获得 [=45] =] 给他们。