我正在编写一个程序,需要分析 C# 代码并弄清楚对于每个方法调用,到底调用的是哪个类的哪个方法。适合这项工作的工具似乎是 Roslyn、CSharpSyntaxTree 和 SemanticModel,但它们工作不可靠。当我在真实代码上运行它时,它在少数情况下找到正确的符号,在更多情况下找到包含正确符号的候选列表,但在大多数情况下,完全失败。
对于可重现的测试用例,这里是分析代码的精炼版本:
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
class Program {
static void Main(string[] _) {
var compilation = CSharpCompilation.Create("MyCompilation")
.WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication))
.AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Environment).Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(
Path.GetDirectoryName(typeof(object).Assembly.Location), "System.Runtime.dll")));
var file = "Class1.cs";
var tree = CSharpSyntaxTree.ParseText(File.ReadAllText(file), CSharpParseOptions.Default, file);
if (tree.GetDiagnostics().Any()) {
foreach (var diagnostic in tree.GetDiagnostics())
Console.Error.WriteLine(diagnostic);
Environment.Exit(1);
}
compilation = compilation.AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
var root = tree.GetCompilationUnitRoot();
new Walker(model).Visit(root);
}
}
class Walker: CSharpSyntaxWalker {
public Walker(SemanticModel model) {
this.model = model;
}
public override void VisitInvocationExpression(InvocationExpressionSyntax node) {
base.VisitInvocationExpression(node);
var symbolInfo = model.GetSymbolInfo(node);
Console.WriteLine(node);
Console.WriteLine(symbolInfo.Symbol);
Console.WriteLine(symbolInfo.CandidateSymbols.ToArray());
}
readonly SemanticModel model;
}
我在这段代码上运行它:
class Class1 {
void Method1() {
Environment.Exit(0);
}
}
这是输出:
Environment.Exit(0)
Microsoft.CodeAnalysis.ISymbol[]
因此,对于对标准库的静态调用,这应该很容易解决,它找不到候选符号。
我错过了什么?
您正在寻找 InspirationExpressionSyntax。在您的示例中没有方法调用。我用过这个例子:
using System;
using System.Text;
namespace ConsoleApplication1
{
class MyClass
{
void Method(string input)
{
var x = new ArgumentNullException();
}
void MethodB(string input)
{
Method("TestMe");
}
void MethodC(string input)
{
MethodB("TestYou");
}
}
}
我的代码如下:
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
class Program
{
static void Main(string[] _)
{
var file = "Class1.cs";
var tree = CSharpSyntaxTree.ParseText(File.ReadAllText(file), CSharpParseOptions.Default, file);
if (tree.GetDiagnostics().Any())
{
foreach (var diagnostic in tree.GetDiagnostics())
Console.Error.WriteLine(diagnostic);
Environment.Exit(1);
}
var mscorlib = MetadataReference.CreateFromFile(Path.Combine(
Path.GetDirectoryName(typeof(object).Assembly.Location), "System.Runtime.dll"));
var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { mscorlib });
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();
var objectCreationExpression = root.DescendantNodes().OfType<InvocationExpressionSyntax>().ToList();
var lstNodes = objectCreationExpression;
foreach (var item in lstNodes)
{
new Walker(semanticModel).Visit(item);
}
}
}
class Walker : CSharpSyntaxWalker
{
public Walker(SemanticModel model)
{
this.model = model;
}
public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
base.VisitInvocationExpression(node);
var symbolInfo = model.GetSymbolInfo(node);
Console.WriteLine(node);
Console.WriteLine(symbolInfo.Symbol);
Console.WriteLine(symbolInfo.CandidateSymbols.ToArray());
}
readonly SemanticModel model;
}