Skip to content

Race in Roslyn API for source generated files causing them to get stuck #80714

@TessenR

Description

@TessenR

Version Used:

Microsoft Visual Studio Professional
Version 17.14.16
Version 17.14.16 Preview 1.0
Version: 18.0.0 Insiders [11104.47]

Steps to Reproduce:

  1. Subscribe to WorkspaceChanged event either directly or via RegisterWorkspaceChangedHandler
  2. Call Microsoft.CodeAnalysis.Project.GetSourceGeneratedDocumentsAsync on projects from Worksspace.CurrentSolution in response to some workspace events like SolutionChanged

Expected Behavior:
You can safely request source generated files via the public Roslyn API

Actual Behavior:
Sometimes, calling Project.GetSourceGeneratedDocumentsAsync causes Roslyn to cache an outdated version of source generators until some future event like solution build forces an updated.
All subsequent calls to Project.GetSourceGeneratedDocumentsAsync return outdated version.
This affect VS's own code analysis as it will see outdated source generated documents, e.g. after navigating to a file that uses them

Notes:
I did some investigation and it looks like the following race happens inside Roslyn:

  • There are 2 concurrent activities in Roslyn:
    • Workspace.EnqueueUpdateSourceGeneratorVersion in response to finishing a build
    • ProjectSystemProjectFactory.StartRefreshingAnalyzerReferenceForFileAsync in response to a file system event about changed analyzer reference assembly
  • During build, Workspace.EnqueueUpdateSourceGeneratorVersion tirggers first and updates source generators' version for the solution.
  • Here, if you call Project.GetSourceGeneratedDocumentsAsync Roslyn will use the old version of the generator but will cache it with the updated SourceGeneratorExecutionVersion since it was already incremented by the previous step
  • Afterwards, a file system watcher event triggers ProjectSystemProjectFactory.StartRefreshingAnalyzerReferenceForFileAsync which reloads the generator without incrementing SourceGeneratorExecutionVersion
  • All future requests to Project.GetSourceGeneratedDocumentsAsync will return the cached outdated version of source generated documents

It looks impossible to tell when it's safe to call Project.GetSourceGeneratedDocumentsAsync because
there's no guarantee when an async refresh of an analyzer reference file will happen and whether it will happen at all after some workspace change

It's also not possible to determine that such a reload happened. It sends a ProjectChanged workspace change event, but checking changes between old and new solution returns empty diff as the project just removes and adds the same analyzer reference in a single modification.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions