I've followed the documentation to create a basic incremental source generator that outputs a copy of classes marked with an attribute:
// Code found in my project
[Copy]
public class MyClass
{
public int Value { get; set; }
}
// Output from the source generator
public class Copy_MyClass
{
public int Value { get; set; }
}
The source generation itself works perfectly fine, but I've run into two problems when using it with Visual Studio 2022:
- The source generator only executes during builds.
- Intellisense doesn't pick up the generated code until I restart Visual Studio.
- I can work around this by having the generated source files written to the project directory, but it's not ideal and still suffers from the above issue: changes don't reflect until I try to build.
For example, if I create a new class Foo
with the [Copy] attribute and then try to reference Copy_Foo
in my code, Visual Studio will display an error that Copy_Foo
is undefined.
[Copy]
public class Foo { }
public static class TestCopy
{
public static void Test()
{
Copy_Foo foo = new Copy_Foo();
// ^^^^^^^^
// The type or namespace 'Copy_Foo' could not be found
}
}
Building this code will succeed, but Visual Studio will continue to think that Copy_Foo
doesn't exist. Once I restart, it will know that Copy_Foo
exists, but any changes to it won't be picked up until I restart again.
I've created a small example, which outputs only the [Copy] attribute.
The first project contains the source generator:
// TestSourceGenerator.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
// TestSourceGenerator.cs
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace TestSourceGenerator
{
[Generator]
public class TestSourceGenerator : IIncrementalGenerator
{
private const string CopyAttributeSource = @"
namespace Generated
{
public class CopyAttribute : System.Attribute { }
}";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(ctx => ctx.AddSource("CopyAttribute.g.cs", SourceText.From(CopyAttributeSource, Encoding.UTF8)));
}
}
}
And the second consumes the generator:
// TestSourceGenerator.Test.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\TestSourceGenerator\TestSourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
// MyClass.cs
namespace TestSourceGenerator.Test
{
[Generated.CopyAttribute]
public class MyClass
{
}
}
If you create the TestSourceGenerator.Test project without the ProjectReference
, open it in Visual Studio, and then edit the project file to reference the analyzer, you'll see that Visual Studio displays an error on [Generated.CopyAttribute]
, but the project builds successfully.
Is there any way to make Visual Studio both run my source generators in the IDE and pick up symbols from the generated code? According to the documentation, it seems like this is supposed to be a supported use case, and even one of the primary motivations for incremental source generators existing.
CreateSyntaxProvider
produces a different set of syntax elements. – Finned