In-memory CSharpCompilation cannot resolve attributes
Asked Answered
D

1

12

I am trying to run C# source generators in-memory using the following code snippet:

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 is set to the necessary sources to compile the code:

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
);

While the generator runs just fine this way, it sadly doesn't have the desired affect, because the source generator relies on an attribute to know whether to generate source for the specified type. The exact source is something along the following:

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

The source generator picks up the attribute correctly, but it gets resolved to an ExtendedErrorTypeSymbol with the result kind being NotAnAttributeType. However, the extended error type symbol also has a candidate symbol, which is the exact symbol I expect it to match.

This is surprising to me, because clearly the type is an attribute, and running the source generator as part of the normal compilation actually does generate all the right types. This seems to imply that there is something strange going on because of the in-memory nature of this run specifically.

As far as I can tell, my list of References covers everything that is needed to correctly realise that something is an attribute (mscorlib, System.Runtime, netstandard, and System.Core), though perhaps there is another MetadataReference missing?

I did find this GitHub issue which seems to describe a very similar, if not the same problem.

I'd love to know if I did something wrong here, if there are other references I am missing, or whether I am missing something completely else altogether.

Duffel answered 2/7, 2021 at 21:18 Comment(0)
D
9

By decompiling the source and stepping through how the attribute become an ExtendedErrorTypeSymbol, I found out that alongside the resultKind and candidate type, you could also find a DiagnosticBag. In this bag, the actual problem was shown:

error CS0012: The type 'Attribute' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

I was under the impression that my code added that correctly, but using this error I was luckily able to further my search, and ran into this Stackoverflow question. This seems to imply that for some reason (I am not 100% sure why), the following code will not actually add the right reference to System.Runtime:

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

Instead, I followed the example of the answer linked above, and changed the code to:

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")),
  MetadataReference.CreateFromFile(Path.Combine(dotNetAssemblyPath, "System.Core.dll")),
  MetadataReference.CreateFromFile(Path.Combine(dotNetAssemblyPath, "System.Private.CoreLib.dll")),
  MetadataReference.CreateFromFile(Path.Combine(dotNetAssemblyPath, "System.Runtime.dll")),

  // more references, loaded as before
);

For some reason, this made all the difference. I had to also add the reference to System.Private.CoreLib.dll, but now that I knew where to find the diagnostics bag with additional information about what is actually wrong, this was an easy fix.

Duffel answered 3/7, 2021 at 16:5 Comment(2)
Hi Tom, the fix is not working me :( So where can I find the DiagnosticBag, do not see it either?Quackery
Found the fix: https://mcmap.net/q/456939/-roslyn-has-no-reference-to-system-runtimeQuackery

© 2022 - 2024 — McMap. All rights reserved.