-
Notifications
You must be signed in to change notification settings - Fork 805
Commit
seq
before {…}
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. | ||
|
||
namespace Microsoft.VisualStudio.FSharp.Editor | ||
|
||
open System.Collections.Immutable | ||
open System.Composition | ||
open FSharp.Compiler.Syntax | ||
open FSharp.Compiler.Text | ||
open Microsoft.CodeAnalysis.CodeFixes | ||
open Microsoft.CodeAnalysis.Text | ||
open CancellableTasks | ||
|
||
[<Sealed>] | ||
[<ExportCodeFixProvider(FSharpConstants.FSharpLanguageName, Name = CodeFix.ChangeToUpcast); Shared>] | ||
type internal AddMissingSeqCodeFixProvider() = | ||
inherit CodeFixProvider() | ||
|
||
static let title = SR.AddMissingSeq() | ||
static let fixableDiagnosticIds = ImmutableArray.Create("FS3873", "FS0740") | ||
|
||
override _.FixableDiagnosticIds = fixableDiagnosticIds | ||
|
||
override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix this | ||
|
||
override this.GetFixAllProvider() = this.RegisterFsharpFixAll() | ||
|
||
interface IFSharpCodeFixProvider with | ||
member _.GetCodeFixIfAppliesAsync context = | ||
cancellableTask { | ||
let! sourceText = context.GetSourceTextAsync() | ||
let! parseFileResults = context.Document.GetFSharpParseResultsAsync(nameof AddMissingSeqCodeFixProvider) | ||
|
||
let getSourceLineStr line = | ||
sourceText.Lines[Line.toZ line].ToString() | ||
|
||
let range = | ||
RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) | ||
|
||
let needsParens = | ||
(range.Start, parseFileResults.ParseTree) | ||
||> ParsedInput.exists (fun path node -> | ||
match path, node with | ||
| SyntaxNode.SynExpr outer :: _, SyntaxNode.SynExpr(expr & SynExpr.ComputationExpr _) when | ||
expr.Range |> Range.equals range | ||
-> | ||
let seqRange = | ||
range | ||
|> Range.withEnd (Position.mkPos range.Start.Line (range.Start.Column + 3)) | ||
|
||
let inner = | ||
SynExpr.App( | ||
ExprAtomicFlag.NonAtomic, | ||
false, | ||
SynExpr.Ident(Ident(nameof seq, seqRange)), | ||
expr, | ||
Range.unionRanges seqRange expr.Range | ||
) | ||
|
||
let outer = | ||
match outer with | ||
| SynExpr.App(flag, isInfix, funcExpr, _, outerAppRange) -> | ||
SynExpr.App(flag, isInfix, funcExpr, inner, outerAppRange) | ||
| outer -> outer | ||
|
||
inner | ||
|> SynExpr.shouldBeParenthesizedInContext getSourceLineStr (SyntaxNode.SynExpr outer :: path) | ||
| _ -> false) | ||
|
||
let text = sourceText.ToString(TextSpan(context.Span.Start, context.Span.Length)) | ||
let newText = if needsParens then $"(seq {text})" else $"seq {text}" | ||
|
||
return | ||
ValueSome | ||
{ | ||
Name = CodeFix.AddMissingSeq | ||
Message = title | ||
Changes = [ TextChange(context.Span, newText) ] | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. | ||
|
||
module FSharp.Editor.Tests.CodeFixes.AddMissingSeqTests | ||
|
||
open Microsoft.VisualStudio.FSharp.Editor | ||
open Xunit | ||
open CodeFixTestFramework | ||
|
||
let private codeFix = AddMissingSeqCodeFixProvider() | ||
|
||
// This can be changed to Auto when featureDeprecatePlacesWhereSeqCanBeOmitted is out of preview. | ||
let mode = WithOption "--langversion:preview" | ||
|
||
[<Fact>] | ||
let ``FS3873 — Adds missing seq before { start..finish }`` () = | ||
let code = "let xs = { 1..10 }" | ||
|
||
let expected = | ||
Some | ||
{ | ||
Message = "Add missing 'seq'" | ||
FixedCode = "let xs = seq { 1..10 }" | ||
} | ||
|
||
let actual = codeFix |> tryFix code mode | ||
|
||
Assert.Equal(expected, actual) | ||
|
||
[<Fact>] | ||
let ``FS0740 — Adds missing seq before { x; y }`` () = | ||
let code = "let xs = { 1; 10 }" | ||
|
||
let expected = | ||
Some | ||
{ | ||
Message = "Add missing 'seq'" | ||
FixedCode = "let xs = seq { 1; 10 }" | ||
} | ||
|
||
let actual = codeFix |> tryFix code mode | ||
|
||
Assert.Equal(expected, actual) | ||
|
||
[<Fact>] | ||
let ``FS3873 — Adds parens when needed`` () = | ||
let code = "let xs = id { 1..10 }" | ||
|
||
let expected = | ||
Some | ||
{ | ||
Message = "Add missing 'seq'" | ||
FixedCode = "let xs = id (seq { 1..10 })" | ||
} | ||
|
||
let actual = codeFix |> tryFix code mode | ||
|
||
Assert.Equal(expected, actual) | ||
|
||
[<Fact>] | ||
let ``FS0740 — Adds parens when needed`` () = | ||
let code = "let xs = id { 1; 10 }" | ||
|
||
let expected = | ||
Some | ||
{ | ||
Message = "Add missing 'seq'" | ||
FixedCode = "let xs = id (seq { 1; 10 })" | ||
} | ||
|
||
let actual = codeFix |> tryFix code mode | ||
|
||
Assert.Equal(expected, actual) | ||
|
||
[<Fact>] | ||
let ``FS3873 — Adds parens when needed — multiline`` () = | ||
let code = | ||
""" | ||
let xs = | ||
id { | ||
1..10 | ||
} | ||
""" | ||
|
||
let expected = | ||
Some | ||
{ | ||
Message = "Add missing 'seq'" | ||
FixedCode = | ||
""" | ||
let xs = | ||
id (seq { | ||
1..10 | ||
}) | ||
""" | ||
} | ||
|
||
let actual = codeFix |> tryFix code mode | ||
|
||
Assert.Equal(expected, actual) | ||
|
||
[<Fact>] | ||
let ``FS0740 — Adds parens when needed — multiline`` () = | ||
let code = | ||
""" | ||
let xs = | ||
id { | ||
1; 10 | ||
} | ||
""" | ||
|
||
let expected = | ||
Some | ||
{ | ||
Message = "Add missing 'seq'" | ||
FixedCode = | ||
""" | ||
let xs = | ||
id (seq { | ||
1; 10 | ||
}) | ||
""" | ||
} | ||
|
||
let actual = codeFix |> tryFix code mode | ||
|
||
Assert.Equal(expected, actual) |