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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions