Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 1a68b45

Browse files
authored
Add 'RemoveUnusedBinding' code fixer (dotnet#11289)
1 parent 0d472e5 commit 1a68b45

17 files changed

+168
-1
lines changed

CodeFix/RemoveUnusedBinding.fs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace Microsoft.VisualStudio.FSharp.Editor
4+
5+
open System
6+
open System.Composition
7+
open System.Threading.Tasks
8+
9+
open Microsoft.CodeAnalysis.Text
10+
open Microsoft.CodeAnalysis.CodeFixes
11+
12+
open FSharp.Compiler.CodeAnalysis
13+
open FSharp.Compiler.Syntax
14+
open FSharp.Compiler.Text
15+
16+
[<AutoOpen>]
17+
module FSharpParseFileResultsExtensions =
18+
type FSharpParseFileResults with
19+
member this.TryRangeOfBindingWithHeadPatternWithPos pos =
20+
let input = this.ParseTree
21+
SyntaxTraversal.Traverse(pos, input, { new SyntaxVisitorBase<_>() with
22+
member _.VisitExpr(_, _, defaultTraverse, expr) =
23+
defaultTraverse expr
24+
25+
override _.VisitBinding(_path, defaultTraverse, binding) =
26+
match binding with
27+
| SynBinding(_, SynBindingKind.Normal, _, _, _, _, _, pat, _, _, _, _) as binding ->
28+
if Position.posEq binding.RangeOfHeadPattern.Start pos then
29+
Some binding.RangeOfBindingWithRhs
30+
else
31+
// Check if it's an operator
32+
match pat with
33+
| SynPat.LongIdent(LongIdentWithDots([id], _), _, _, _, _, _) when id.idText.StartsWith("op_") ->
34+
if Position.posEq id.idRange.Start pos then
35+
Some binding.RangeOfBindingWithRhs
36+
else
37+
defaultTraverse binding
38+
| _ -> defaultTraverse binding
39+
40+
| _ -> defaultTraverse binding })
41+
42+
[<ExportCodeFixProvider(FSharpConstants.FSharpLanguageName, Name = "RemoveUnusedBinding"); Shared>]
43+
type internal FSharpRemoveUnusedBindingCodeFixProvider
44+
[<ImportingConstructor>]
45+
(
46+
checkerProvider: FSharpCheckerProvider,
47+
projectInfoManager: FSharpProjectOptionsManager
48+
) =
49+
50+
inherit CodeFixProvider()
51+
static let userOpName = "RemoveUnusedBinding"
52+
let fixableDiagnosticIds = set ["FS1182"]
53+
let checker = checkerProvider.Checker
54+
55+
override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds
56+
57+
override _.RegisterCodeFixesAsync context : Task =
58+
asyncMaybe {
59+
// Don't show code fixes for unused values, even if they are compiler-generated.
60+
do! Option.guard context.Document.FSharpOptions.CodeFixes.UnusedDeclarations
61+
62+
let document = context.Document
63+
let! sourceText = document.GetTextAsync(context.CancellationToken)
64+
65+
let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName)
66+
let! parseResults = checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName = userOpName) |> liftAsync
67+
68+
let diagnostics =
69+
context.Diagnostics
70+
|> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id)
71+
|> Seq.toImmutableArray
72+
73+
let symbolRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText)
74+
let! rangeOfBinding = parseResults.TryRangeOfBindingWithHeadPatternWithPos(symbolRange.Start)
75+
let! spanOfBinding = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, rangeOfBinding)
76+
77+
let keywordEndColumn =
78+
let rec loop ch pos =
79+
if not (Char.IsWhiteSpace(ch)) then
80+
pos
81+
else
82+
loop sourceText.[pos - 1] (pos - 1)
83+
loop sourceText.[spanOfBinding.Start - 1] (spanOfBinding.Start - 1)
84+
85+
// This is safe, since we could never have gotten here unless there was a `let` or `use`
86+
let keywordStartColumn = keywordEndColumn - 2
87+
let fullSpan = TextSpan(keywordStartColumn, spanOfBinding.End - keywordStartColumn)
88+
89+
let prefixTitle = SR.RemoveUnusedBinding()
90+
let removalCodeFix =
91+
CodeFixHelpers.createTextChangeCodeFix(
92+
prefixTitle,
93+
context,
94+
(fun () -> asyncMaybe.Return [| TextChange(fullSpan, "") |]))
95+
context.RegisterCodeFix(removalCodeFix, diagnostics)
96+
}
97+
|> Async.Ignore
98+
|> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken)

CodeFix/RenameUnusedValue.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type internal FSharpRenameUnusedValueCodeFixProvider
3535
do! Option.guard context.Document.FSharpOptions.CodeFixes.UnusedDeclarations
3636

3737
let document = context.Document
38-
let! sourceText = document.GetTextAsync()
38+
let! sourceText = document.GetTextAsync(context.CancellationToken)
3939
let ident = sourceText.ToString(context.Span)
4040
// Prefixing operators and backticked identifiers does not make sense.
4141
// We have to use the additional check for backtickes because `IsOperatorOrBacktickedName` operates on display names

FSharp.Editor.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
<Compile Include="CodeFix\AddOpenCodeFixProvider.fs" />
113113
<Compile Include="CodeFix\ProposeUppercaseLabel.fs" />
114114
<Compile Include="CodeFix\ReplaceWithSuggestion.fs" />
115+
<Compile Include="CodeFix\RemoveUnusedBinding.fs" />
115116
<Compile Include="CodeFix\RenameUnusedValue.fs" />
116117
<Compile Include="CodeFix\ImplementInterfaceCodeFixProvider.fs" />
117118
<Compile Include="CodeFix\SimplifyName.fs" />

FSharp.Editor.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,4 +279,7 @@
279279
<data name="AddTypeAnnotation" xml:space="preserve">
280280
<value>Add type annotation</value>
281281
</data>
282+
<data name="RemoveUnusedBinding" xml:space="preserve">
283+
<value>Remove unused binding</value>
284+
</data>
282285
</root>

xlf/FSharp.Editor.cs.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@
9797
<target state="translated">Odebrat return!</target>
9898
<note />
9999
</trans-unit>
100+
<trans-unit id="RemoveUnusedBinding">
101+
<source>Remove unused binding</source>
102+
<target state="new">Remove unused binding</target>
103+
<note />
104+
</trans-unit>
100105
<trans-unit id="RemoveYield">
101106
<source>Remove 'yield'</source>
102107
<target state="translated">Odebrat yield</target>

xlf/FSharp.Editor.de.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@
9797
<target state="translated">"return!" entfernen</target>
9898
<note />
9999
</trans-unit>
100+
<trans-unit id="RemoveUnusedBinding">
101+
<source>Remove unused binding</source>
102+
<target state="new">Remove unused binding</target>
103+
<note />
104+
</trans-unit>
100105
<trans-unit id="RemoveYield">
101106
<source>Remove 'yield'</source>
102107
<target state="translated">"yield" entfernen</target>

xlf/FSharp.Editor.es.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@
9797
<target state="translated">Quitar "return!"</target>
9898
<note />
9999
</trans-unit>
100+
<trans-unit id="RemoveUnusedBinding">
101+
<source>Remove unused binding</source>
102+
<target state="new">Remove unused binding</target>
103+
<note />
104+
</trans-unit>
100105
<trans-unit id="RemoveYield">
101106
<source>Remove 'yield'</source>
102107
<target state="translated">Quitar "yield"</target>

xlf/FSharp.Editor.fr.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@
9797
<target state="translated">Supprimer 'return!'</target>
9898
<note />
9999
</trans-unit>
100+
<trans-unit id="RemoveUnusedBinding">
101+
<source>Remove unused binding</source>
102+
<target state="new">Remove unused binding</target>
103+
<note />
104+
</trans-unit>
100105
<trans-unit id="RemoveYield">
101106
<source>Remove 'yield'</source>
102107
<target state="translated">Supprimer 'yield'</target>

xlf/FSharp.Editor.it.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@
9797
<target state="translated">Rimuovi 'return!'</target>
9898
<note />
9999
</trans-unit>
100+
<trans-unit id="RemoveUnusedBinding">
101+
<source>Remove unused binding</source>
102+
<target state="new">Remove unused binding</target>
103+
<note />
104+
</trans-unit>
100105
<trans-unit id="RemoveYield">
101106
<source>Remove 'yield'</source>
102107
<target state="translated">Rimuovi 'yield'</target>

xlf/FSharp.Editor.ja.xlf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@
9797
<target state="translated">'return!' の削除</target>
9898
<note />
9999
</trans-unit>
100+
<trans-unit id="RemoveUnusedBinding">
101+
<source>Remove unused binding</source>
102+
<target state="new">Remove unused binding</target>
103+
<note />
104+
</trans-unit>
100105
<trans-unit id="RemoveYield">
101106
<source>Remove 'yield'</source>
102107
<target state="translated">'yield' の削除</target>

0 commit comments

Comments
 (0)