From 7baab2c9f3101a8a828824f35f9719ce6e02950b Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 22 Jun 2023 15:11:54 +0200 Subject: [PATCH 1/3] Refactoring and tests for the ConvertCSharpLambdaToFSharpLambda code fix --- .../ConvertCSharpLambdaToFSharpLambda.fs | 61 ++++++++++++------- .../CodeFixes/CodeFixTestFramework.fs | 2 +- .../ConvertCSharpLambdaToFSharpLambdaTests.fs | 46 ++++++++++++++ .../FSharp.Editor.Tests.fsproj | 1 + 4 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs index 978f8e328f1..1cf6bb0c69e 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs @@ -8,38 +8,57 @@ open System.Collections.Immutable open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Text + +open CancellableTasks + [] type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] () = inherit CodeFixProvider() static let title = SR.UseFSharpLambda() + let tryGetSpans (parseResults: FSharpParseFileResults) (range: range) sourceText = + let bind3 options = + match options with + | Some (Some a, Some b, Some c) -> Some(a, b, c) + | _ -> None + + parseResults.TryRangeOfParenEnclosingOpEqualsGreaterUsage range.Start + |> Option.map (fun (fullParenRange, lambdaArgRange, lambdaBodyRange) -> + RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, fullParenRange), + RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lambdaArgRange), + RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lambdaBodyRange)) + |> bind3 + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039", "FS0043") - override _.RegisterCodeFixesAsync context = - asyncMaybe { - let! parseResults = - context.Document.GetFSharpParseResultsAsync(nameof (ConvertCSharpLambdaToFSharpLambdaCodeFixProvider)) - |> liftAsync + override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) + interface IFSharpCodeFixProvider with + member _.GetCodeFixIfAppliesAsync document span = + cancellableTask { + let! cancellationToken = CancellableTask.getCurrentCancellationToken () - let errorRange = - RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) + let! parseResults = document.GetFSharpParseResultsAsync(nameof (ConvertCSharpLambdaToFSharpLambdaCodeFixProvider)) - let! fullParenRange, lambdaArgRange, lambdaBodyRange = - parseResults.TryRangeOfParenEnclosingOpEqualsGreaterUsage errorRange.Start + let! sourceText = document.GetTextAsync(cancellationToken) - let! fullParenSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, fullParenRange) - let! lambdaArgSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lambdaArgRange) - let! lambdaBodySpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, lambdaBodyRange) + let errorRange = + RoslynHelpers.TextSpanToFSharpRange(document.FilePath, span, sourceText) - let replacement = - let argText = sourceText.GetSubText(lambdaArgSpan).ToString() - let bodyText = sourceText.GetSubText(lambdaBodySpan).ToString() - TextChange(fullParenSpan, "fun " + argText + " -> " + bodyText) + return + tryGetSpans parseResults errorRange sourceText + |> Option.map (fun (fullParenSpan, lambdaArgSpan, lambdaBodySpan) -> + let replacement = + let argText = sourceText.GetSubText(lambdaArgSpan).ToString() + let bodyText = sourceText.GetSubText(lambdaBodySpan).ToString() + TextChange(fullParenSpan, "fun " + argText + " -> " + bodyText) - do context.RegisterFsharpFix(CodeFix.ConvertCSharpLambdaToFSharpLambda, title, [| replacement |]) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + { + Name = CodeFix.ConvertCSharpLambdaToFSharpLambda + Message = title + Changes = [ replacement ] + }) + } diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs index 616bc907fe5..542039ccc58 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/CodeFixTestFramework.fs @@ -20,7 +20,7 @@ let getRelevantDiagnostic (document: Document) errorNumber = return checkFileResults.Diagnostics |> Seq.where (fun d -> d.ErrorNumber = errorNumber) - |> Seq.exactlyOne + |> Seq.head } let tryFix (code: string) diagnostic (fixProvider: IFSharpCodeFixProvider) = diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs new file mode 100644 index 00000000000..f51ab834ad7 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Editor.Tests.CodeFixes.ConvertCSharpLambdaToFSharpLambdaTests + +open Microsoft.VisualStudio.FSharp.Editor +open Xunit + +open CodeFixTestFramework + +let private codeFix = ConvertCSharpLambdaToFSharpLambdaCodeFixProvider() + +let private diagnostic = 0039 // Something is not defined + +[] +let ``Fixes FS0039 for lambdas`` () = + let code = + """ +let id = x => x +""" + + let expected = + Some + { + Message = "Use F# lambda syntax" + FixedCode = + """ +let id = fun x -> x +""" + } + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) + +[] +let ``Doesn't fix FS0039 for random undefined stuff`` () = + let code = + """ +let test = x = x +""" + + let expected = None + + let actual = codeFix |> tryFix code diagnostic + + Assert.Equal(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj index a943a2932cb..47be31b4652 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -38,6 +38,7 @@ + From 1702bef2bb4ed3b831a07ab48d2259d999accea1 Mon Sep 17 00:00:00 2001 From: Petr Date: Mon, 26 Jun 2023 16:32:08 +0200 Subject: [PATCH 2/3] Update ConvertCSharpLambdaToFSharpLambda.fs --- .../CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs index 1cf6bb0c69e..f6dfb2bf42a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs @@ -32,7 +32,7 @@ type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [ bind3 - override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039", "FS0043") + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) From dc0dc2cc0a741b8f3248edd7124e3094896fd166 Mon Sep 17 00:00:00 2001 From: Petr Date: Tue, 27 Jun 2023 16:26:52 +0200 Subject: [PATCH 3/3] up --- .../CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs | 4 ++-- .../CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs index f6dfb2bf42a..7028b9b5afe 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertCSharpLambdaToFSharpLambda.fs @@ -20,7 +20,7 @@ type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [ Some(a, b, c) | _ -> None @@ -30,7 +30,7 @@ type internal ConvertCSharpLambdaToFSharpLambdaCodeFixProvider [ bind3 + |> flatten3 override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs index f51ab834ad7..3c66ceb5370 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertCSharpLambdaToFSharpLambdaTests.fs @@ -15,7 +15,7 @@ let private diagnostic = 0039 // Something is not defined let ``Fixes FS0039 for lambdas`` () = let code = """ -let id = x => x +let incAll = List.map (n => n + 1) """ let expected = @@ -24,7 +24,7 @@ let id = x => x Message = "Use F# lambda syntax" FixedCode = """ -let id = fun x -> x +let incAll = List.map (fun n -> n + 1) """ } @@ -36,7 +36,7 @@ let id = fun x -> x let ``Doesn't fix FS0039 for random undefined stuff`` () = let code = """ -let test = x = x +let f = g """ let expected = None