无法从编译对象获取SyntaxTree

时间:2019-03-06 11:10:48

标签: c# roslyn roslyn-code-analysis

我是roslyn的初学者,因此我尝试通过制作一个非常简单的控制台应用程序来开始学习它,该应用程序在著名的教程网站中进行了介绍。 (https://riptutorial.com/roslyn/example/16545/introspective-analysis-of-an-analyzer-in-csharp),效果不佳。

我创建的Cosole应用程序是.NET Framework(目标Framework版本是4.7.2),而不是.NET Core或.NET标准。 我添加了NuGet包Microsoft.CodeAnalysis和Microsoft.CodeAnalysis.Workspaces.MSBuild,然后编写了一个简单的代码,如下所示。

using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using System;
using System.Linq;

namespace SimpleRoslynConsole
{
class Program
    {
        static void Main(string[] args)
        {
            // Declaring a variable with the current project file path.
            // *** You have to change this path to fit your development environment.
            const string projectPath =
                @"C:\Users\[MyName]\Source\Repos\RoslynTrialConsole01\RoslynTrialConsole01.csproj";
            var workspace = MSBuildWorkspace.Create();
            var project = workspace.OpenProjectAsync(projectPath).Result;
            // [**1]Getting the compilation.
            var compilation = project.GetCompilationAsync().Result;
            // [**2]As this is a simple single file program, the first syntax tree will be the current file.
            var syntaxTree = compilation.SyntaxTrees.FirstOrDefault();
            if (syntaxTree != null)
            {
                var rootSyntaxNode = syntaxTree.GetRootAsync().Result;
                var firstLocalVariablesDeclaration = rootSyntaxNode.DescendantNodesAndSelf()
                    .OfType<LocalDeclarationStatementSyntax>().First();
                var firstVariable = firstLocalVariablesDeclaration.Declaration.Variables.First();
                var variableInitializer = firstVariable.Initializer.Value.GetFirstToken().ValueText;
                Console.WriteLine(variableInitializer);
            }
            else
            {
                Console.WriteLine("Could not get SyntaxTrees from this projects.");
            }
            Console.WriteLine("Hit any key.");
            Console.ReadKey();
        }
    }
}

我的问题是,Compilation对象的SyntaxTrees属性在[** 2]标记中返回null。自然,遵循FirstOrDefault方法将返回null。

我尝试了其他一些代码。我发现可以使用CSharpSyntaxTree.ParseText方法从CSharp代码文本中获取SyntaxTree。但是按照

的顺序,我无法从源代码中获取任何信息
var workspace = MSBuildWorkspace.Create();
var project = workspace.OpenProjectAsync(projectPath).Result;
var compilation = project.GetCompilationAsync().Result;

我想知道的是,如果我错过了使用上述过程从源代码中获取语法信息的事情。

我会感谢有人给我一个很好的建议。

3 个答案:

答案 0 :(得分:1)

我认为问题在于 .net 框架项目的源文件路径位于 .csproj 中。并立即打开项目。 对于 .net 核心项目,您没有此类信息,也许这就是 Workspace 实例不知道要加载什么,因此什么都不加载的原因。 至少将 .cs 文件指定为添加的文档就可以了。尝试应用此:

static class ProjectExtensions
{
    public static Project AddDocuments(this Project project, IEnumerable<string> files)
    {
        foreach (string file in files)
        {
            project = project.AddDocument(file, File.ReadAllText(file)).Project;
        }
        return project;
    }

    private static IEnumerable<string> GetAllSourceFiles(string directoryPath)
    {
        var res = Directory.GetFiles(directoryPath, "*.cs", SearchOption.AllDirectories);

        return res;
    }


    public static Project WithAllSourceFiles(this Project project)
    {
        string projectDirectory = Directory.GetParent(project.FilePath).FullName;
        var files = GetAllSourceFiles(projectDirectory);
        var newProject = project.AddDocuments(files);
        return newProject;
    }
}

WithAllsourceFiles 方法将返回您的项目,该项目的编译将依次包含您期望的所有语法树,就像您在 Visual Studio 中一样

答案 1 :(得分:0)

除非您在应用程序的app.config文件中具有与msbuild.exe.config相同的重定向,否则MsBuildWorkspace将无法正常工作。如果没有重定向,则可能无法加载msbuild库。您需要找到系统上的msbuild.exe.config文件,并将与Microsoft.Build程序集相关的<assemblyBinding>元素复制到app.config中。确保将它们放置在正确的元素配置/运行时下。

答案 2 :(得分:0)

我在网上搜索了各种示例程序,发现了最可靠,最安全的方法。解决方案是创建一个静态方法,该方法将在指定的File中返回SyntaxTrees,如下所示。

private static Compilation CreateTestCompilation()
{
    var found = false;
    var di = new DirectoryInfo(Environment.CurrentDirectory);
    var fi = di.GetFiles().Where((crt) => { return crt.Name.Equals("program.cs", StringComparison.CurrentCultureIgnoreCase); }).FirstOrDefault();
    while ((fi == null) || (di.Parent == null))
    {
        di = new DirectoryInfo(di.Parent.FullName);
        fi = di.GetFiles().Where((crt) => { return crt.Name.Equals("program.cs", StringComparison.CurrentCultureIgnoreCase); }).FirstOrDefault();
        if (fi != null)
        {
            found = true;
            break;
        }
    }
    if (!found)
    {
        return null;
    }
    var targetPath = di.FullName +  @"\Program.cs";
    var targetText = File.ReadAllText(targetPath);
    var targetTree =
                   CSharpSyntaxTree.ParseText(targetText)
                                   .WithFilePath(targetPath);
    var target2Path = di.FullName + @"\TypeInferenceRewriter.cs";
    var target2Text = File.ReadAllText(target2Path);
    var target2Tree =
                   CSharpSyntaxTree.ParseText(target2Text)
                                   .WithFilePath(target2Path);

    SyntaxTree[] sourceTrees = { programTree, target2Tree };

    MetadataReference mscorlib =
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
    MetadataReference codeAnalysis =
            MetadataReference.CreateFromFile(typeof(SyntaxTree).Assembly.Location);
    MetadataReference csharpCodeAnalysis =
            MetadataReference.CreateFromFile(typeof(CSharpSyntaxTree).Assembly.Location);

    MetadataReference[] references = { mscorlib, codeAnalysis, csharpCodeAnalysis };

    return CSharpCompilation.Create("TransformationCS",
                                    sourceTrees,
                                    references,
                                    new CSharpCompilationOptions(
                                            OutputKind.ConsoleApplication));
}

调用者程序将是这样。

static void Main(string[] args)
{
    var test = CreateTestCompilation();
    if (test == null)
    {
        return;
    }

    foreach (SyntaxTree sourceTree in test.SyntaxTrees)
    {
        Console.WriteLine(souceTree.ToFullString());
    }
}

当然,需要进行很多改进才能使其实用。