From aee19e6c8e35f2834d2aa524af37e465af46a828 Mon Sep 17 00:00:00 2001 From: cartermp Date: Sun, 15 Nov 2020 16:23:51 -0800 Subject: [PATCH 1/2] Add ChangeRefCellDerefToNotExpression code fixer --- src/fsharp/service/ServiceUntypedParse.fs | 18 +++++++ src/fsharp/service/ServiceUntypedParse.fsi | 7 +++ tests/service/ServiceUntypedParseTests.fs | 53 +++++++++++++++++++ .../ChangeRefCellDerefToNotExpression.fs | 52 ++++++++++++++++++ .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../src/FSharp.Editor/FSharp.Editor.resx | 3 ++ .../FSharp.Editor/xlf/FSharp.Editor.cs.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.de.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.es.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.fr.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.it.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.ja.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.ko.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.pl.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.ru.xlf | 5 ++ .../FSharp.Editor/xlf/FSharp.Editor.tr.xlf | 5 ++ .../xlf/FSharp.Editor.zh-Hans.xlf | 5 ++ .../xlf/FSharp.Editor.zh-Hant.xlf | 5 ++ 19 files changed, 199 insertions(+) create mode 100644 vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index bd24fef3fad..0513f0a2fa4 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -100,6 +100,24 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option member scope.ParseTree = input + member scope.TryRangeOfRefCellDereferenceContainingPos expressionPos = + match input with + | Some input -> + let res = + AstTraversal.Traverse(expressionPos, input, { new AstTraversal.AstVisitorBase<_>() with + member _.VisitExpr(_, _, defaultTraverse, expr) = + match expr with + | SynExpr.App(_, false, SynExpr.Ident funcIdent, expr, _) -> + if funcIdent.idText = "op_Dereference" && rangeContainsPos expr.Range expressionPos then + Some funcIdent.idRange + else + None + | _ -> defaultTraverse expr + }) + res + | None -> + None + member scope.FindNoteworthyParamInfoLocations pos = match input with | Some input -> FSharpNoteworthyParamInfoLocations.Find(pos, input) diff --git a/src/fsharp/service/ServiceUntypedParse.fsi b/src/fsharp/service/ServiceUntypedParse.fsi index b88bc0efec3..96b783ecf3f 100755 --- a/src/fsharp/service/ServiceUntypedParse.fsi +++ b/src/fsharp/service/ServiceUntypedParse.fsi @@ -19,6 +19,13 @@ type public FSharpParseFileResults = /// The syntax tree resulting from the parse member ParseTree : ParsedInput option + /// + /// Given the position of an expression, attempts to find the range of the + /// '!' in a derefence operation of that expression, like: + /// '!expr', '!(expr)', etc. + /// + member TryRangeOfRefCellDereferenceContainingPos: expressionPos: pos -> Option + /// Notable parse info for ParameterInfo at a given location member FindNoteworthyParamInfoLocations : pos:pos -> FSharpNoteworthyParamInfoLocations option diff --git a/tests/service/ServiceUntypedParseTests.fs b/tests/service/ServiceUntypedParseTests.fs index 18c7f7318d1..07d31f9a38e 100644 --- a/tests/service/ServiceUntypedParseTests.fs +++ b/tests/service/ServiceUntypedParseTests.fs @@ -305,3 +305,56 @@ type T = new (x:int) = () """ getTypeMemberRange source |> shouldEqual [ (3, 4), (3, 20) ] + + +[] +let ``TryRangeOfRefCellDereferenceContainingPos - simple``() = + let source = """ +let x = false +let y = !x +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.TryRangeOfRefCellDereferenceContainingPos (mkPos 3 9) + match res with + | Some res -> + res + |> tups + |> fst + |> shouldEqual (3, 8) + | None -> + Assert.Fail("No deref operator found in source.") + +[] +let ``TryRangeOfRefCellDereferenceContainingPos - parens``() = + let source = """ +let x = false +let y = !(x) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.TryRangeOfRefCellDereferenceContainingPos (mkPos 3 10) + match res with + | Some res -> + res + |> tups + |> fst + |> shouldEqual (3, 8) + | None -> + Assert.Fail("No deref operator found in source.") + + +[] +let ``TryRangeOfRefCellDereferenceContainingPos - binary expr``() = + let source = """ +let x = false +let y = !(x = false) +""" + let parseFileResults, _ = getParseAndCheckResults source + let res = parseFileResults.TryRangeOfRefCellDereferenceContainingPos (mkPos 3 10) + match res with + | Some res -> + res + |> tups + |> fst + |> shouldEqual (3, 8) + | None -> + Assert.Fail("No deref operator found in source.") \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs new file mode 100644 index 00000000000..0d7b80e9d96 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor + +open System.Composition +open System.Threading.Tasks + +open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.CodeFixes + +[] +type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider + [] + ( + checkerProvider: FSharpCheckerProvider, + projectInfoManager: FSharpProjectOptionsManager + ) = + inherit CodeFixProvider() + + static let userOpName = "FSharpChangeRefCellDerefToNotExpressionCodeFix" + let fixableDiagnosticIds = set ["FS0001"] + + override __.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds + + override this.RegisterCodeFixesAsync context : Task = + asyncMaybe { + let document = context.Document + let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) + let! sourceText = context.Document.GetTextAsync(context.CancellationToken) + let! parseResults = checkerProvider.Checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName) |> liftAsync + + let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) + let! derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos errorRange.Start + let! derefSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, derefRange) + + let title = SR.UseNotForNegation() + + let diagnostics = + context.Diagnostics + |> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id) + |> Seq.toImmutableArray + + let codeFix = + CodeFixHelpers.createTextChangeCodeFix( + title, + context, + (fun () -> asyncMaybe.Return [| TextChange(derefSpan, "not ") |])) + + context.RegisterCodeFix(codeFix, diagnostics) + } + |> Async.Ignore + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 8bc59c35a19..3f71d6266bc 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -89,6 +89,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx index 8440136cf33..7644167019f 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.resx +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.resx @@ -219,4 +219,7 @@ F# Dispostable Values (top-level) + + Use 'not' to negate expression + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf index acac2c13bf4..56f1f675bfa 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf @@ -172,6 +172,11 @@ Formátování + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf index 639a34858b9..31c9ed6e180 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf @@ -172,6 +172,11 @@ Formatierung + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf index 5f1297eab36..4e6f2ae971d 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf @@ -172,6 +172,11 @@ Formato + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf index 3ae9d25817a..d9d3481c7c8 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf @@ -172,6 +172,11 @@ Mise en forme + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf index 8457ca172b1..30c3aa83579 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf @@ -172,6 +172,11 @@ Formattazione + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf index 614fdea4a51..fd63c4a5132 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf @@ -172,6 +172,11 @@ 書式設定 + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf index 832aaa80b5c..1a7cedfd811 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf @@ -172,6 +172,11 @@ 서식 + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf index 785230a56bb..df421f7b236 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf @@ -172,6 +172,11 @@ Formatowanie + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf index 98636e84fd4..ba045cadb1d 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf @@ -172,6 +172,11 @@ Formatação + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf index 04891b9acea..f979a1438c1 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf @@ -172,6 +172,11 @@ Форматирование + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf index 0cbe170c6d7..e503c35fdad 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf @@ -172,6 +172,11 @@ Biçimlendirme + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf index dd827dbfd65..c5e11c4cdc4 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hans.xlf @@ -172,6 +172,11 @@ 正在格式化 + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf index a59aa79970f..760c93883f7 100644 --- a/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf +++ b/vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.zh-Hant.xlf @@ -172,6 +172,11 @@ 格式化 + + Use 'not' to negate expression + Use 'not' to negate expression + + \ No newline at end of file From 44d563eb82f626aa940d89271bc3bb90336e5a48 Mon Sep 17 00:00:00 2001 From: cartermp Date: Sun, 15 Nov 2020 19:29:12 -0800 Subject: [PATCH 2/2] Area --- tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs index 1f94595981f..1bdb7e77681 100644 --- a/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs +++ b/tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs @@ -22735,6 +22735,7 @@ FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Boolean get_ParseHadE FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: FSharp.Compiler.SourceCodeServices.FSharpErrorInfo[] Errors FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: FSharp.Compiler.SourceCodeServices.FSharpErrorInfo[] get_Errors() FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: FSharp.Compiler.SourceCodeServices.FSharpNavigationItems GetNavigationItems() +FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Range+range] TryRangeOfRefCellDereferenceContainingPos(pos) FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Range+range] ValidateBreakpointLocation(pos) FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.SourceCodeServices.FSharpNoteworthyParamInfoLocations] FindNoteworthyParamInfoLocations(pos) FSharp.Compiler.SourceCodeServices.FSharpParseFileResults: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.SyntaxTree+ParsedInput] ParseTree