I have a C# 9.0 source code generator that references a class library that contains an attribute that is used to identify the classes to process. I am now writing unit tests as described here:
Compilation inputCompilation = CreateCompilation(@"
using Dependncy;
namespace MyCode
{
[Marker]
public partial class SimpleObject
{
[PropertyMarker(0)]
public int Test{get;set;}
}
}
");
var generator = new MyCodeGen();
// Create the driver that will control the generation, passing in our generator
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
// Run the generation pass
// (Note: the generator driver itself is immutable, and all calls return an updated version of the driver that you should use for subsequent calls)
driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);
// We can now assert things about the resulting compilation:
Assert.IsTrue(diagnostics.IsEmpty); // there were no diagnostics created by the generators
Assert.IsTrue(outputCompilation.SyntaxTrees.Count() == 2); // we have two syntax trees, the original 'user' provided one, and the one added by the generator
Assert.IsTrue(outputCompilation.GetDiagnostics().IsEmpty); // verify the compilation with the added source has no diagnostics
// Or we can look at the results directly:
GeneratorDriverRunResult runResult = driver.GetRunResult();
// The runResult contains the combined results of all generators passed to the driver
Assert.IsTrue(runResult.GeneratedTrees.Length == 1);
Assert.IsTrue(runResult.Diagnostics.IsEmpty);
// Or you can access the individual results on a by-generator basis
GeneratorRunResult generatorResult = runResult.Results[0];
Assert.IsTrue(generatorResult.Generator == generator);
Assert.IsTrue(generatorResult.Diagnostics.IsEmpty);
Assert.IsTrue(generatorResult.GeneratedSources.Length == 1);
Assert.IsTrue(generatorResult.Exception is null);
CreateCompilation
looks like this
private static Compilation CreateCompilation(string source)
=> CSharpCompilation.Create("compilation",
new[] { CSharpSyntaxTree.ParseText(source) },
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location), MetadataReference.CreateFromFile(typeof(MarkerAttribute).GetTypeInfo().Assembly.Location) },
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
However, the generator will not recognize the marker attribute (symbols are not equal). I guess, the issue is, that within the compilation, they are treated as from another assembly: One from the compilation within the UnitTest and the one that is references in the source code generator.
The source code generator obtains the symbol to compare with compilation.GetTypeByMetadataName(typeof(MarkerAttribute).FullName)
but also references the containing assembly itself. I include the reference with GetDependencyTargetPaths
as they do it in the official sample csproj.
Any idea how I have to create the compilation, so that the symbols are equal in the source generator?
Edit 1
I just found out, that the AttributeClass
seems to match the type (i.e. Names are equal), but its Kind Property returns SymbolKind.ErrorType
while the one obtained by the source gen with compilation.GetTypeByMetadataName(typeof(MarkerAttribute).FullName)
returns SymbolKind.NamedType
. So that's probably why they don't match. However, I still have no idea why the symbols are resolved differently.