diff --git a/global.json b/global.json index 80540bb70..52227659a 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,6 @@ { "sdk": { - "version": "5.0.301" + "version": "5.0.301", + "rollForward": "feature" } } diff --git a/src/FsAutoComplete/CodeFixes/UnusedValue.fs b/src/FsAutoComplete/CodeFixes/UnusedValue.fs index 63b35e932..ea14a3d37 100644 --- a/src/FsAutoComplete/CodeFixes/UnusedValue.fs +++ b/src/FsAutoComplete/CodeFixes/UnusedValue.fs @@ -13,17 +13,17 @@ let fix (getRangeText: GetRangeText) = "is unused" (fun diagnostic codeActionParams -> asyncResult { - match getRangeText (codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath) diagnostic.Range with + match getRangeText (codeActionParams.TextDocument.GetFilePath() |> normalizePath) diagnostic.Range with | Ok unusedExpression -> match diagnostic.Code with | Some _ -> return [ { SourceDiagnostic = Some diagnostic File = codeActionParams.TextDocument - Title = "Replace with __" + Title = "Replace with _" Edits = [| { Range = diagnostic.Range - NewText = "__" } |] + NewText = "_" } |] Kind = Refactor } ] | None -> let replaceSuggestion = "_" diff --git a/test/FsAutoComplete.Tests.Lsp/CodeFixTests.fs b/test/FsAutoComplete.Tests.Lsp/CodeFixTests.fs index f1f46ea21..2b1fff7d0 100644 --- a/test/FsAutoComplete.Tests.Lsp/CodeFixTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/CodeFixTests.fs @@ -205,6 +205,108 @@ let missingInstanceMemberTests state = }) ] +let unusedValueTests state = + let (|Refactor|_|) title newText action = + match action with + | { Title = title' + Kind = Some "refactor" + Edit = { DocumentChanges = Some [| + { Edits = [| { NewText = newText' } |] } + |] } + } + when title' = title && newText' = newText -> Some () + | _ -> None + + let (|ActReplace|_|) = (|Refactor|_|) "Replace with _" "_" + + let (|ActPrefix|_|) oldText = (|Refactor|_|) "Prefix with _" $"_{oldText}" + + let server = + async { + let path = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "UnusedValue") + let cfg = { defaultConfigDto with UnusedDeclarationsAnalyzer = Some true } + let! (server, events) = serverInitialize path cfg state + do! waitForWorkspaceFinishedParsing events + let path = Path.Combine(path, "Script.fsx") + let tdop : DidOpenTextDocumentParams = { TextDocument = loadDocument path } + do! server.TextDocumentDidOpen tdop + let! diagnostics = + events + |> waitForFsacDiagnosticsForFile "Script.fsx" + |> AsyncResult.bimap (fun _ -> failtest "Should have had errors") id + return (server, path, diagnostics) + } + |> Async.Cache + + let canReplaceUnusedSelfReference = testCaseAsync "can replace unused self-reference" (async { + let! server, file, diagnostics = server + let diagnostic = + diagnostics + |> Array.tryFind (fun d -> + d.Range.Start = { Line = 2; Character = 9 } && + d.Range.End = { Line = 2; Character = 13 } + ) |> Option.defaultWith (fun () -> failwith "could not find diagnostic with expected range") + let detected = { + CodeActionParams.TextDocument = { Uri = Path.FilePathToUri file } + Range = diagnostic.Range + Context = { Diagnostics = [| diagnostic |] } + } + match! server.TextDocumentCodeAction detected with + | Ok (Some (TextDocumentCodeActionResult.CodeActions [| ActReplace |])) -> () + | Ok other -> + failtestf $"Should have generated _, but instead generated %A{other}" + | Error reason -> + failtestf $"Should have succeeded, but failed with %A{reason}" + }) + + let canReplaceUnusedBinding = testCaseAsync "can replace unused binding" (async { + let! server, file, diagnostics = server + let diagnostic = + diagnostics + |> Array.tryFind (fun d -> + d.Range.Start = { Line = 9; Character = 4 } && + d.Range.End = { Line = 9; Character = 7 } + ) |> Option.defaultWith (fun () -> failwith "could not find diagnostic with expected range") + let detected = { + CodeActionParams.TextDocument = { Uri = Path.FilePathToUri file } + Range = diagnostic.Range + Context = { Diagnostics = [| diagnostic |] } + } + match! server.TextDocumentCodeAction detected with + | Ok (Some (TextDocumentCodeActionResult.CodeActions [| ActReplace; ActPrefix "six" |])) -> () + | Ok other -> + failtestf $"Should have generated _, but instead generated %A{other}" + | Error reason -> + failtestf $"Should have succeeded, but failed with %A{reason}" + }) + + let canReplaceUnusedParameter = testCaseAsync "can replace unused parameter" (async { + let! server, file, diagnostics = server + let diagnostic = + diagnostics + |> Array.tryFind (fun d -> + d.Range.Start = { Line = 15; Character = 16 } && + d.Range.End = { Line = 15; Character = 21 } + ) |> Option.defaultWith (fun () -> failwith "could not find diagnostic with expected range") + let detected = { + CodeActionParams.TextDocument = { Uri = Path.FilePathToUri file } + Range = diagnostic.Range + Context = { Diagnostics = [| diagnostic |] } + } + match! server.TextDocumentCodeAction detected with + | Ok (Some (TextDocumentCodeActionResult.CodeActions [| ActReplace; ActPrefix "three" |])) -> () + | Ok other -> + failtestf $"Should have generated _, but instead generated %A{other}" + | Error reason -> + failtestf $"Should have succeeded, but failed with %A{reason}" + }) + + testList "unused value" [ + canReplaceUnusedSelfReference + canReplaceUnusedBinding + canReplaceUnusedParameter + ] + let tests state = testList "codefix tests" [ abstractClassGenerationTests state generateMatchTests state @@ -212,4 +314,5 @@ let tests state = testList "codefix tests" [ outerBindingRecursiveTests state nameofInsteadOfTypeofNameTests state missingInstanceMemberTests state + unusedValueTests state ] diff --git a/test/FsAutoComplete.Tests.Lsp/Helpers.fs b/test/FsAutoComplete.Tests.Lsp/Helpers.fs index 800663de3..af77bfc2a 100644 --- a/test/FsAutoComplete.Tests.Lsp/Helpers.fs +++ b/test/FsAutoComplete.Tests.Lsp/Helpers.fs @@ -421,6 +421,11 @@ let waitForParseResultsForFile file = >> diagnosticsToResult >> Async.AwaitObservable +let waitForFsacDiagnosticsForFile file = + fsacDiagnostics file + >> diagnosticsToResult + >> Async.AwaitObservable + let waitForParsedScript (event: ClientEvents) = event |> typedEvents "textDocument/publishDiagnostics" diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/UnusedValue/Script.fsx b/test/FsAutoComplete.Tests.Lsp/TestCases/UnusedValue/Script.fsx new file mode 100644 index 000000000..1f2971f6a --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/UnusedValue/Script.fsx @@ -0,0 +1,16 @@ +(* unused self reference *) +type MyClass() = + member this.DoAThing() = () + + +(* + replace usused binding with _ + prefix _ to unused binding +*) +let six = 6 + +(* + replace usused function parameter with _ + prefix _ to unused function parameter +*) +let add one two three = one + two