roslyn: Source Generators: design-time completion/intellisense is never fixed

Version Used:

I copied the global.json from the samples. I’m using Visual Studio Community 2019 - Version 16.6.0 Preview 6.0 (just installed).

Steps to Reproduce:

  1. Unzip the attached repro: generator.zip
  2. There are two sets of binlogs collected with the Project System Tools: after the first build of the solution, after restarting VS as indicated in the known issues in the announcement.
  3. After closing VS, deleting the .vs directory and cleaning all bin/obj folders and opening VS and doing another build.

Expected Behavior: At least after restarting, the completion is fixed. Worst case, it’s fixed after a restart.

Actual Behavior: Build works fine, but completion is never fixed, no matter how many times I restart VS 😦

Workaround:

Add the following to your project:

WARNING: using this workaround is likely to severely impair performance in ways that are very difficult to identify and correct later. It should not be used.
  <PropertyGroup>
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="$(CompilerGeneratedFilesOutputPath)\**" />
  </ItemGroup>
  
  <Target Name="RemoveSourceGeneratedFiles" BeforeTargets="CoreCompile">
    <ItemGroup>
      <Compile Remove="$(CompilerGeneratedFilesOutputPath)\**" />
    </ItemGroup>
  </Target>

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 11
  • Comments: 65 (28 by maintainers)

Most upvoted comments

@kzu, in my generator, i workaround also by writting all the time generated files, but i skip them when building using an msbuild target run just before compilation :

  <Target Name="RemoveSourceGeneratedFiles" BeforeTargets="CoreCompile">
    <ItemGroup>
      <Compile Remove="**/*.SourceGenerated.cs" />
    </ItemGroup>
  </Target>

my generated files are suffixed with SourceGenerated.cs

Thus, i can navigate to generated code with VS, and have working intellisense and debugging experience…

NOTICE:

The workaround here is to fix Rider 2021.1 ONLY If you are having issues with Visual Studio, update to 16.10+!

Original Post:

I got building and intellisense working by doing the following:

    <PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>
    <Target Name="AddSourceGeneratedFiles" AfterTargets="CoreCompile">
        <ItemGroup>
            <Compile Include="Generated\**" />
        </ItemGroup>
    </Target>
    <Target Name="RemoveSourceGeneratedFiles" BeforeTargets="CoreCompile">
        <ItemGroup>
            <Compile Remove="Generated\**" />
        </ItemGroup>
    </Target>

If you want to have the generated folder cleaned during rebuild, add this:

    <Target Name="CleanSourceGeneratedFiles" AfterTargets="Clean">
        <RemoveDir Directories="Generated" />
    </Target

(everything just works)

Everything absolutely does not work for in-solution source generators. Without any workarounds, every time I open my solution it’s basically a random dice roll whether Intellisense (including error squiggles) actually works for things affected by the source generator. It fails more often than not.

If it wasn’t for my workaround, I would’ve had to move my source generator back to being a manually-run command line tool ages ago.

I tried using the solution from the initial post above, but it’s built against a preview release of source generators which is no longer supported.

@Sergio0694 has a more recent repro here: https://github.com/dotnet/roslyn/issues/51810

Some source generators generate code that doesn’t change often. In such cases it is often desirable to put generated code into files that can be checked in, and only updated if the output of the source generator changes. It makes little sense to run such generators on every edit in Visual Studio.

While I agree with the premise, the conclusion here is flawed. The intent of IIncrementalGenerator is exactly to eliminate the need to re-run the full source generator when the output did not change. It does not need to save source files to disk in order to achieve this goal.

To the contrary, it has proven extremely difficult to author customized build logic which successfully generates source on demand prior to the compilation step and then correctly ties into the many implementations of up-to-date checks.

I’m extremely concerned about the suggestions regarding <EmitCompilerGeneratedFiles> and <CompilerGeneratedFilesOutputPath>. These properties are intended for the convenience of a project maintainer and must not be controlled by a source generator package. I’ve edited the comment to make it more difficult for someone to copy/paste this text and cause downstream bugs that we have to go back and work through.

@CorrM keep in mind that is an unsupported scenario that is known to break. It might sometimes appear correct, but other work in progress directly conflicts with it we’ve seen it can be extremely difficult to understand which it suddenly stops working in strange ways.

Added my current workaround to the issue description.

This workaround will cause a severe productivity impairment (it breaks all of design time builds, incremental solution updates, and the fast up-to-date check). It is imperative that this workaround not be used. I updated the post to strongly discourage use of the workaround to help avoid an onslaught of performance-related feedback that doesn’t make sense in the context of these features.

This solution looks perfect @kamronbatman but I’m getting Ambiguous reference errors between classes defined in the disk files and the definitions offered by the source generator, am I missing anything obvious?

I remember getting that when I forgot to use partial. Make sure both your user code, and generated code are using partial classes. If you are still getting the error then it is possible you have duplicate code somewhere. This also happened to me in some cases. Cleaning the source generated folder and regenerating fixed it.

To troubleshoot I would actually manually modify the generated code to look like how I want and eliminate the errors, then update the codegen to match.

Thanks for the feedback @kamronbatman it’s really appreciated. Ive had a dig through the issues and I think my problem is closer linked to #48083 where VS is caching the source generator and intellisense isn’t picking up changes to the source generator code (although the build does) until I restart visual studio.

Thanks again

This solution looks perfect @kamronbatman but I’m getting Ambiguous reference errors between classes defined in the disk files and the definitions offered by the source generator, am I missing anything obvious?

I remember getting that when I forgot to use partial. Make sure both your user code, and generated code are using partial classes. If you are still getting the error then it is possible you have duplicate code somewhere. This also happened to me in some cases. Cleaning the source generated folder and regenerating fixed it.

To troubleshoot I would actually manually modify the generated code to look like how I want and eliminate the errors, then update the codegen to match.

(Anyone who decided to use my workaround above should definitely get the updated version that includes a check for CompilerGeneratedFilesOutputPath being defined. I forgot to check it and someone found out the hard way when they got into a situation where it wasn’t defined and MSBuild happily deleted every C# source file on their C: drive.)

@u7pro your technique worked perfectly. Thanks

@b-straub ide support for source generators is a work in progress.

@jasonmalinowski to look into the underlying issue.

In terms of writing out to disk etc, its a scenario we’re actively working on: https://github.com/dotnet/roslyn/projects/54#card-34161504

There are still multiple issues pending to be resolved.

Even when I’ve made intellisense working, it was still slow if you have 30+ classes in solution. in .NET 6 there is incremental generator which should help with performance (https://andrewlock.net/exploring-dotnet-6-part-9-source-generator-updates-incremental-generators/) but for me even this didn’t work as I’m loading and parsing classes from another csproj. Reported issue: https://github.com/dotnet/runtime/issues/56702

Currently, my workaround is to get it working as I had behaviour with https://github.com/AArnott/CodeGeneration.Roslyn

So, package reference is excluded from design time: <PackageReference Condition="'$(DesignTimeBuild)'!='true'" Include="X.Core.Generator" OutputItemType="Analyzer" Version="$(XCoreNugetVersion)" />

Then, for build I’m using:

	<PropertyGroup>
	    <IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
	    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
	    <CompilerGeneratedFilesOutputPath>GeneratedFiles</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>
    <Target Name="RemoveSourceGeneratedFiles" BeforeTargets="BeforeBuild">
	    <ItemGroup>
		    <Compile Remove="GeneratedFiles\**" />
	    </ItemGroup>
    </Target>

this way, I have intellisense and performance is not impacted.

i see problems now, so there a better work around .?

Almost all cases have been fixed for the 16.10 release, and the remaining ones tend to be less severe than the problems introduced by workarounds. If you have a detailed repro case we can take a look (best to file a new issue and tag me and @chsienki).