测试源生成器
Testing source generator
我正在尝试测试源代码生成器。
发电机:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context) { }
public void Execute(GeneratorExecutionContext context)
{
//...
}
}
测试代码:
Compilation inputCompilation = CreateCompilation(@"
namespace MyCode
{
public class Program
{
public static void Main(string[] args)
{
}
}
}
");
var generator = new CustomGenerator();
var driver = CSharpGeneratorDriver.Create(generator);
driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);
static Compilation CreateCompilation(string source)
=> CSharpCompilation.Create("compilation",
new[] { CSharpSyntaxTree.ParseText(source) },
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
问题是 context.SyntaxReceiver 总是为空,如何解决?
让我们检查 GeneratorExecutionContext.SyntaxReceiver 属性:
的定义
namespace Microsoft.CodeAnalysis
{
public readonly struct GeneratorExecutionContext
{
// Summary:
// If the generator registered an Microsoft.CodeAnalysis.ISyntaxReceiver during
// initialization, this will be the instance created for this generation pass.
public ISyntaxReceiver? SyntaxReceiver { get; }
}
}
我们看到 GeneratorExecutionContext.SyntaxReceiver
属性 被声明为 nullable,这意味着在某些情况下它确实应该为 null。
文档注释显示我们首先必须注册我们的 ISyntaxReceiver in ISourceGenerator.Initialize.
作为canton7 has pointed out, we first need to register it via the GeneratorInitializationContext.RegisterForSyntaxNotifications方法:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(static () => new CustomReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
Debug.Assert(context.SyntaxReceiver is not null, $"No {nameof(ISyntaxReceiver)} registerd via {nameof(GeneratorInitializationContext.RegisterForSyntaxNotifications)}.");
if (context.SyntaxReceiver is not CustomReceiver receiver)
{
return;
}
Debug.Assert(receiver is not null);
//...
}
}
internal sealed class CustomReceiver : ISyntaxReceiver
{
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
}
}
记住要避免在 ISyntaxReceiver
中进行昂贵的操作,因为它不像 Generator 本身那样支持 取消。
我正在尝试测试源代码生成器。 发电机:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context) { }
public void Execute(GeneratorExecutionContext context)
{
//...
}
}
测试代码:
Compilation inputCompilation = CreateCompilation(@"
namespace MyCode
{
public class Program
{
public static void Main(string[] args)
{
}
}
}
");
var generator = new CustomGenerator();
var driver = CSharpGeneratorDriver.Create(generator);
driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);
static Compilation CreateCompilation(string source)
=> CSharpCompilation.Create("compilation",
new[] { CSharpSyntaxTree.ParseText(source) },
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
问题是 context.SyntaxReceiver 总是为空,如何解决?
让我们检查 GeneratorExecutionContext.SyntaxReceiver 属性:
的定义namespace Microsoft.CodeAnalysis
{
public readonly struct GeneratorExecutionContext
{
// Summary:
// If the generator registered an Microsoft.CodeAnalysis.ISyntaxReceiver during
// initialization, this will be the instance created for this generation pass.
public ISyntaxReceiver? SyntaxReceiver { get; }
}
}
我们看到 GeneratorExecutionContext.SyntaxReceiver
属性 被声明为 nullable,这意味着在某些情况下它确实应该为 null。
文档注释显示我们首先必须注册我们的 ISyntaxReceiver in ISourceGenerator.Initialize.
作为canton7 has pointed out, we first need to register it via the GeneratorInitializationContext.RegisterForSyntaxNotifications方法:
[Generator]
public class CustomGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(static () => new CustomReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
Debug.Assert(context.SyntaxReceiver is not null, $"No {nameof(ISyntaxReceiver)} registerd via {nameof(GeneratorInitializationContext.RegisterForSyntaxNotifications)}.");
if (context.SyntaxReceiver is not CustomReceiver receiver)
{
return;
}
Debug.Assert(receiver is not null);
//...
}
}
internal sealed class CustomReceiver : ISyntaxReceiver
{
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
}
}
记住要避免在 ISyntaxReceiver
中进行昂贵的操作,因为它不像 Generator 本身那样支持 取消。