diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MissingReference.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MissingReference.fs new file mode 100644 index 00000000000..eb77d13c355 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/CodeFixes/MissingReference.fs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor + +open System +open System.Composition +open System.Collections.Immutable +open System.IO + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.CodeFixes +open Microsoft.CodeAnalysis.CodeActions + +open CancellableTasks + +type private ReferenceType = + | AddProjectRef of ProjectReference + | AddMetadataRef of MetadataReference + +// This code fix only works for the legacy, non-SDK projects. +// In SDK projects, transitive references do this trick. +// See: https://github.com/dotnet/fsharp/pull/2743 + +// Because this code fix is barely applicable anymore +// and because it's very different from other code fixes +// (applies to the projects files, not code itself), +// it's not implemented via IFSharpCodeFix interfaces +// and not tested automatically either. +// If we happen to create similar code fixes, +// it'd make sense to create some testing framework for them. +[] +type internal MissingReferenceCodeFixProvider() = + inherit CodeFixProvider() + + let createCodeFix (title: string, context: CodeFixContext, addReference: ReferenceType) = + CodeAction.Create( + title, + (fun cancellationToken -> + cancellableTask { + let project = context.Document.Project + let solution = project.Solution + + match addReference with + | AddProjectRef projectRef -> + let references = project.AllProjectReferences + let newReferences = references |> Seq.append [ projectRef ] + return solution.WithProjectReferences(project.Id, newReferences) + + | AddMetadataRef metadataRef -> + let references = project.MetadataReferences + let newReferences = references |> Seq.append [ metadataRef ] + return solution.WithProjectMetadataReferences(project.Id, newReferences) + } + |> CancellableTask.start cancellationToken) + ) + + override _.FixableDiagnosticIds = ImmutableArray.Create "FS0074" + + override _.RegisterCodeFixesAsync context = + cancellableTask { + let solution = context.Document.Project.Solution + + let diagnostic = context.Diagnostics[0] + let message = diagnostic.GetMessage() + let parts = message.Split([| '\'' |], StringSplitOptions.None) + + match parts with + | [| _; _type; _; assemblyName; _ |] -> + + let exactProjectMatches = + solution.Projects + |> Seq.tryFindV (fun project -> + String.Compare(project.AssemblyName, assemblyName, StringComparison.OrdinalIgnoreCase) = 0) + + match exactProjectMatches with + | ValueSome refProject -> + let codefix = + createCodeFix ( + String.Format(SR.AddProjectReference(), refProject.Name), + context, + AddProjectRef(ProjectReference refProject.Id) + ) + + context.RegisterCodeFix(codefix, ImmutableArray.Create diagnostic) + | ValueNone -> + let metadataReferences = + solution.Projects + |> Seq.collect (fun project -> project.MetadataReferences) + |> Seq.tryFindV (fun ref -> + let referenceAssemblyName = Path.GetFileNameWithoutExtension(ref.Display) + String.Compare(referenceAssemblyName, assemblyName, StringComparison.OrdinalIgnoreCase) = 0) + + match metadataReferences with + | ValueSome metadataRef -> + let codefix = + createCodeFix (String.Format(SR.AddAssemblyReference(), assemblyName), context, AddMetadataRef metadataRef) + + context.RegisterCodeFix(codefix, ImmutableArray.Create diagnostic) + | ValueNone -> () + | _ -> () + } + |> CancellableTask.startAsTask context.CancellationToken diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/MissingReferenceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFixes/MissingReferenceCodeFixProvider.fs deleted file mode 100644 index eabc0cd2880..00000000000 --- a/vsintegration/src/FSharp.Editor/CodeFixes/MissingReferenceCodeFixProvider.fs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open System -open System.Composition -open System.Collections.Immutable -open System.Threading -open System.Threading.Tasks -open System.IO - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.CodeFixes -open Microsoft.CodeAnalysis.CodeActions - -type private ReferenceType = - | AddProjectRef of ProjectReference - | AddMetadataRef of MetadataReference - -[] -type internal MissingReferenceCodeFixProvider() = - inherit CodeFixProvider() - - let fixableDiagnosticId = "FS0074" - - let createCodeFix (title: string, context: CodeFixContext, addReference: ReferenceType) = - CodeAction.Create( - title, - (fun (cancellationToken: CancellationToken) -> - async { - let project = context.Document.Project - let solution = project.Solution - - match addReference with - | AddProjectRef projectRef -> - let references = project.AllProjectReferences - let newReferences = references |> Seq.append [ projectRef ] - return solution.WithProjectReferences(project.Id, newReferences) - - | AddMetadataRef metadataRef -> - let references = project.MetadataReferences - let newReferences = references |> Seq.append [ metadataRef ] - return solution.WithProjectMetadataReferences(project.Id, newReferences) - } - |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), - title - ) - - override _.FixableDiagnosticIds = Seq.toImmutableArray [ fixableDiagnosticId ] - - override _.RegisterCodeFixesAsync context : Task = - async { - let solution = context.Document.Project.Solution - - context.Diagnostics - |> Seq.filter (fun x -> x.Id = fixableDiagnosticId) - |> Seq.iter (fun diagnostic -> - let message = diagnostic.GetMessage() - let parts = message.Split([| '\'' |], StringSplitOptions.None) - - match parts with - | [| _; _type; _; assemblyName; _ |] -> - - let exactProjectMatches = - solution.Projects - |> Seq.tryFindV (fun project -> - String.Compare(project.AssemblyName, assemblyName, StringComparison.OrdinalIgnoreCase) = 0) - - match exactProjectMatches with - | ValueSome refProject -> - let codefix = - createCodeFix ( - String.Format(SR.AddProjectReference(), refProject.Name), - context, - AddProjectRef(ProjectReference refProject.Id) - ) - - context.RegisterCodeFix(codefix, ImmutableArray.Create diagnostic) - | ValueNone -> - let metadataReferences = - solution.Projects - |> Seq.collect (fun project -> project.MetadataReferences) - |> Seq.tryFindV (fun ref -> - let referenceAssemblyName = Path.GetFileNameWithoutExtension(ref.Display) - String.Compare(referenceAssemblyName, assemblyName, StringComparison.OrdinalIgnoreCase) = 0) - - match metadataReferences with - | ValueSome metadataRef -> - let codefix = - createCodeFix (String.Format(SR.AddAssemblyReference(), assemblyName), context, AddMetadataRef metadataRef) - - context.RegisterCodeFix(codefix, ImmutableArray.Create diagnostic) - | ValueNone -> () - | _ -> ()) - } - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Common/Constants.fs b/vsintegration/src/FSharp.Editor/Common/Constants.fs index d59b241b44e..8e529cf0319 100644 --- a/vsintegration/src/FSharp.Editor/Common/Constants.fs +++ b/vsintegration/src/FSharp.Editor/Common/Constants.fs @@ -163,6 +163,9 @@ module internal CodeFix = [] let MakeDeclarationMutable = "MakeDeclarationMutable" + [] + let MissingReference = "MissingReference" + [] let ChangePrefixNegationToInfixSubtraction = "ChangePrefixNegationToInfixSubtraction" diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index b73a33b690d..b2a685d2347 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -133,7 +133,7 @@ - +