内存中的 CSharpCompilation 无法解析属性

问题描述

我正在尝试使用以下代码片段在内存中运行 C# 源代码生成器:

var SyntaxTree = await SyntaxTreeFromrelativeFile("testdata/IMyInterface.cs");
var compilation = CSharpCompilation.Create("compilation",ImmutableArray.Create(SyntaxTree),References);
var generator = new proxygenerator();

GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

driver = driver.RunGenerators(compilation);

References 设置为编译代码所需的来源:

public static readonly ImmutableArray<MetadataReference> References = ImmutableArray.Create<MetadataReference>(
  // System
  MetadataReference.CreateFromFile(typeof(object).Assembly.Location),MetadataReference.CreateFromFile(typeof(GCSettings).Assembly.Location),MetadataReference.CreateFromFile(typeof(Attribute).Assembly.Location),MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),// JetBrains
  MetadataReference.CreateFromFile(typeof(UsedImplicitlyAttribute).Assembly.Location),// some custom ones
);

虽然生成器以这种方式运行得很好,但遗憾的是它没有达到预期的效果,因为源生成器依赖于一个属性来知道是否为指定类型生成源。确切来源如下:

[MyAttribute]
public interface IMyInterface { /* ... */ }

生成器正确地选择了属性,但它被解析为 ExtendedErrorTypeSymbol,结果类型为 NotAnAttributeType。但是,扩展错误类型符号也有一个候选符号,这是我希望它匹配的确切符号。

这让我感到惊讶,因为显然类型是一个属性,并且将源生成器作为正常编译的一部分运行实际上确实生成了所有正确的类型。这似乎意味着由于这次运行的内存性质,会发生一些奇怪的事情。

据我所知,我的 References 列表涵盖了正确认识到某物是一个属性所需的一切(mscorlibSystem.Runtimenetstandard、和 System.Core),但也许还有一个 MetadataReference 不见了?

我确实发现 this GitHub issue 似乎描述了一个非常相似的问题,如果不是相同的问题。

我很想知道我是否在这里做错了什么,是否还有我遗漏的其他参考资料,或者我是否完全遗漏了其他东西。

解决方法

通过反编译源代码并逐步了解属性如何变成 ExtendedErrorTypeSymbol,我发现除了 resultKind 和候选类型之外,您还可以找到 DiagnosticBag。在这个包中,显示了实际问题:

错误 CS0012:类型“属性”是在未引用的程序集中定义的。您必须添加对程序集“System.Runtime,Version=5.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a”的引用。

我的印象是我的代码正确添加了它,但使用这个错误,我幸运地能够进一步搜索,并遇到了 this Stackoverflow question。这似乎暗示出于某种原因(我不是 100% 确定为什么),以下代码实际上不会添加对 System.Runtime 的正确引用:

MetadataReference.CreateFromFile(typeof(GCSettings).Assembly.Location)

相反,我按照上面链接的答案示例,将代码更改为:

private static readonly string dotNetAssemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);

public static readonly ImmutableArray<MetadataReference> References = ImmutableArray.Create<MetadataReference>(
  // .NET assemblies are finicky and need to be loaded in a special way.
  MetadataReference.CreateFromFile(Path.Combine(dotNetAssemblyPath,"mscorlib.dll")),MetadataReference.CreateFromFile(Path.Combine(dotNetAssemblyPath,"System.dll")),"System.Core.dll")),"System.Private.CoreLib.dll")),"System.Runtime.dll")),// more references,loaded as before
);

出于某种原因,这完全不同。我还必须添加对 System.Private.CoreLib.dll 的引用,但现在我知道在哪里可以找到包含有关实际错误的附加信息的诊断包,这很容易解决。