Skip to content

Populate Roslyn Project.MetadataReferences for SDK-style F# projects #20407

Description

@xperiandri

Symptom

For SDK-style (CPS) F# projects, Project.MetadataReferences in the Roslyn workspace is empty (verified in the debugger: Count = 0). References exist only as -r: strings inside FSharpProjectOptions.OtherOptions, and only after the project has been "activated" (a file opened). Only the legacy project path (LegacyProjectWorkspaceMap) populates real metadata references.

This breaks or degrades:

Root cause chain

  1. F# receives the references and drops them. CPS delivers the design-time command line via the MEF export HandleCommandLineChanges (vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs:570-637). Its signature includes references: ImmutableArray<CommandLineReference>, but only sources and options are forwarded to FSharpProjectOptionsManager.SetCommandLineOptions; the references parameter is discarded.
  2. Roslyn's command-line path is a silent no-op for F#. CPSProject only creates a ProjectSystemProjectOptionsProcessor when an ICommandLineParserService is registered for the language (CPSProject_IWorkspaceProjectContext.cs:91-98); only C# and VB register one, so both SetOptions overloads no-op for F#. CPSProjectFactory.cs:79-105 also explicitly excludes non-C#/VB languages from CommandLineArgsForDesignTimeEvaluation.
  3. Even C# doesn't get references from SetOptions. ProjectSystemProjectOptionsProcessor never applies parsed CommandLineArguments.MetadataReferences; C#/VB references arrive via explicit IWorkspaceProjectContext.AddMetadataReference calls from dotnet/project-system's MetadataReferenceItemHandler — a path the F# project system never wired.
  4. The F# options manager already expects the fix. tryComputeOptions (FSharpProjectOptionsManager.fs:361-380) strips -r: from the CPS command line and re-derives references from project.MetadataReferences / project.ProjectReferences — by design the workspace is meant to be the source of truth; the collection is just never populated on the CPS path.

Proposed fix (two small PRs, no dotnet/project-system change needed)

Because F# already receives the reference list via MEF, the fix does not need project-system involvement:

PR 1 — dotnet/roslyn (ExternalAccess.FSharp). Add a ProjectId-keyed metadata-reference API — e.g. FSharpVisualStudioWorkspaceExtensions.SetMetadataReferences(workspace, projectId, paths) or a TryGetProjectContext(ProjectId) returning the existing IFSharpWorkspaceProjectContext — routed internally to the CPS ProjectSystemProject batch API (AddMetadataReference/RemoveMetadataReference). The shape already exists in FSharpWorkspaceProjectContext.SetMetadataReferences (src/VisualStudio/ExternalAccess/Core/FSharp/VS/IFSharpWorkspaceProjectContextFactory.cs:168-184); what's missing is reaching a CPS-created context by ProjectId.

PR 2 — dotnet/fsharp (after the Roslyn dependency flows). In HandleCommandLineChanges, after the ProjectId is resolved (already done at LanguageService.fs:596-609), push the received references into the workspace via the new API, diffing against the current set so unchanged reference lists are a no-op (precedent: LegacyProjectWorkspaceMap.fs:91-97, SingleFileWorkspaceMap.fs:96). tryComputeOptions then picks the references up with no further change.

Rejected alternative

Registering an ICommandLineParserService for F# in Roslyn: insufficient on its own (see point 3 — the options processor ignores parsed references) and would additionally require project-system changes to pass CommandLineArgsForDesignTimeEvaluation for F#.

Related

#3512, #15961, #20131, #20406

/cc @vzarytovskii @T-Gro @psfinaki @0101

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions