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

Created on 8 May 2020  路  16Comments  路  Source: dotnet/roslyn

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 :(

Area-IDE Concept-Continuous Improvement New Feature - Source Generators

Most helpful comment

@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..

All 16 comments

FYI @chsienki @jasonmalinowski

I see the same. I get a squiggle but build works

My workaround for now is to use the AnalyzerSettings package (won't be needed once we can pass MSBuild properties and items to analyzers, add this (via targets provided by the generator nuget):

    <PropertyGroup>
        <GraphGeneratorExtension>.gql$(GeneratedFileExtension)</GraphGeneratorExtension>
        <DesignTime Condition="'$(DesignTimeBuild)' == 'true' or '$(BuildingProject)' != 'true'">true</DesignTime>
    </PropertyGroup>

    <ItemGroup>
        <!-- To workaround https://github.com/dotnet/roslyn/issues/44093, we'll add the generated 
             source directly from obj directory for the DTB. The real build works fine. -->
        <Compile Include="$(IntermediateOutputPath)*$(GraphGeneratorExtension)"
                Condition="'$(DesignTimeBuild)' == 'true'"
                Link="%(Filename)%(Extension)"
                Visible="false"/>
    </ItemGroup>

    <ItemGroup>
        <AnalyzerSetting Include="DesignTime" Value="$(DesignTime)" />
    </ItemGroup>

My generator uses a specific extension, which I then add only for DTB via the <Compile ... element above. The analyzer settings package brings in the IntermediateOutputPath property to the context which I then use to determine if I need to generate the file on disk or not and the place to leave the files on. The first DTB very briefly fails but the Compile item kicks in right away and brings it into the compilation, fixing the intellisense. During a regular build, I just let the regular source generator run without saving to disk.

            var designTime = 
                settings.TryGetValue("DesignTime", out var value) &&
                bool.TryParse(value, out var dtb) && dtb;

                if (designTime)
                    File.WriteAllText(Path.Combine(objDir, hintName + extension), sourceContent);

@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..

Far simpler :)

@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

This is currently my number one topic. I have started a pretty well working SG to implement a reactive MVU pattern, but the unusable Intellisense makes it uncomfortable to use.

Even after restarting VS, Intellisense will not work in most cases. After editing the code, the generated attributes can be recognized, but the generated properties never work.

_Example:_
ObservableTests

This is currently my number one topic. I have started a pretty well working SG to implement a reactive MVU pattern, but the unusable Intellisense makes it uncomfortable to use.

Even after restarting VS, Intellisense will not work in most cases. After editing the code, the generated attributes can be recognized, but the generated properties never work.

_Example:_
ObservableTests

You don't need to restart VS if you write generated content to files inside the project folder.. VS will recognize them automatically if you're using SDK style projects

But don't forget to remove files before compilation to avoid duplicate compile items error, you can do it easily using the msbuild target above https://github.com/dotnet/roslyn/issues/44093#issuecomment-626248357

You don't need to restart VS if you write generated content to files inside the project folder.. VS will recognize them automatically if you're using SDK style projects

But don't forget to remove files before compilation to avoid duplicate compile items error, you can do it easily using the msbuild target above #44093 (comment)

I know about and also using this approach for testing. But finally when SG will be released as Nugets this will be not an option. I will not be acceptable to clutter a users project with generated files.

You don't need to restart VS if you write generated content to files inside the project folder.. VS will recognize them automatically if you're using SDK style projects
But don't forget to remove files before compilation to avoid duplicate compile items error, you can do it easily using the msbuild target above #44093 (comment)

I know about and also using this approach for testing. But finally when SG will be released as Nugets this will be not an option. I will not be acceptable to clutter a users project with generated files.

it's a workaround for the current preview release, and as mentioned above https://github.com/dotnet/roslyn/issues/44093#issuecomment-626878203, this will be solved for final release

Currently including a generator dll in the csproj like

<Analyzer Include="C:/.../Generator.dll"/>

everything works except intellisense. From reading this issue - this is expected right? I shouldn't be including it in any other way?

Right now the suggested resolution is to write the .cs files to disk but not include them in compile, right?

Approaching .NET 5.0 final release this issue must be fixed.
Still not addressed for RC1.
Emitting the generated source but removing for compilation is currently the only way to make IntelliSense working.
Unfortunately with this approach SG embedded analyzers/codefixes are not kicking in.

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

@u7pro your technique worked perfectly. Thanks

Copy+pasting my workaround from https://github.com/dotnet/roslyn/issues/48083#issuecomment-743137253

This workaround is purely MSBuild-based, so it doesn't require modifying your source generator. I've been using it for about a week with no issues, but it's not heavily tested. It is in the same spirit as @kzu and @u7pro's workarounds where the design-time build in Visual Studio uses the code emitted from the previous build. It only applies to in-solution source generators, and indirectly breaks the design-time experience for in-solution analyzers. (You can change _AnalyzerProjectReferencesToRemove to know about some naming convention if you want to fix that.)

First, enable EmitCompilerGeneratedFiles. (I do this in Directory.Biuld.props so it applies to my entire solution.)

<PropertyGroup>
  <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

Then, in Directory.Build.targets: (Should work in the project file too, did not test.)

<!--
  As a workaround for https://github.com/dotnet/roslyn/issues/44093 we skip source generators for design-time builds
  and emit generated files and include them for design-time builds.
-->
<Target Name="Roslyn44093Workaround" BeforeTargets="CoreCompile;ResolveProjectReferences;$(PrepareProjectReferencesDependsOn)" Condition="'$(DesignTimeBuild)' == 'true'">
    <ItemGroup>
      <!-- This is slightly over-zealous since it removes in-solution analyzers too -->
      <_AnalyzerProjectReferencesToRemove Include="@(ProjectReference)" Condition="'%(OutputItemType)' == 'Analyzer'" />
      <ProjectReference Remove="@(_AnalyzerProjectReferencesToRemove)" />

      <Compile Include="$(CompilerGeneratedFilesOutputPath)/%(_AnalyzerProjectReferencesToRemove.Filename)/**/*.cs" Visible="false" />
    </ItemGroup>
</Target>
<!-- Workaround for https://github.com/dotnet/roslyn/issues/47966 and https://github.com/dotnet/roslyn/issues/49125 -->
<Target Name="Roslyn46966And49125Workaround" BeforeTargets="CoreCompile;Clean" Condition="'$(DesignTimeBuild)' != 'true' and '$(CompilerGeneratedFilesOutputPath)' != ''">
  <ItemGroup>
    <_GeneratedSourceFileToRemove Include="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
  </ItemGroup>
  <Delete Files="@(_GeneratedSourceFileToRemove)" />
</Target>

Since this leverages EmitCompilerGeneratedFiles, you do not need to modify your source generator in any way. This fix intentionally only applies to in-solution generators, so NuGet generators are unaffected.

(Note the included workaround for https://github.com/dotnet/roslyn/issues/47966 and https://github.com/dotnet/roslyn/issues/49125: It's pretty aggressive, so don't use your CompilerGeneratedFilesOutputPath for unrelated files for whatever reason.)

(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.)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MadsTorgersen picture MadsTorgersen  路  542Comments

Neil65 picture Neil65  路  112Comments

MgSam picture MgSam  路  152Comments

MadsTorgersen picture MadsTorgersen  路  249Comments

davidroth picture davidroth  路  158Comments