Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace with DotLambda: relax constraints #712

Merged
merged 2 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ open System
open FSharp.Compiler.Symbols
open FSharp.Compiler.Syntax
open JetBrains.ReSharper.Feature.Services.Daemon
open JetBrains.ReSharper.Plugins.FSharp.ProjectModel
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.Highlightings.Errors
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util.FSharpMethodInvocationUtil
Expand Down Expand Up @@ -278,13 +279,14 @@ type LambdaAnalyzer() =
inner null expr

let getRootRefExprIfCanBeConvertedToDotLambda (pat: ILocalReferencePat) (lambda: ILambdaExpr) =
let isFSharp90Supported = FSharpLanguageLevel.isFSharp90Supported lambda
let expr = lambda.Expression.IgnoreInnerParens()
if not expr.IsSingleLine then null else

let rootRefExpr = getRootRefExpr expr
if isNull rootRefExpr ||
rootRefExpr.ShortName <> pat.SourceName ||
not (isContextWithoutWildPats expr) then null else
not (isFSharp90Supported || isContextWithoutWildPats expr) then null else

let patSymbol = pat.GetFcsSymbol()
let mutable convertingUnsupported = false
Expand All @@ -295,7 +297,7 @@ type LambdaAnalyzer() =
member x.ProcessBeforeInterior(treeNode) =
if treeNode == rootRefExpr then () else
match treeNode with
| :? IDotLambdaExpr -> convertingUnsupported <- true
| :? IDotLambdaExpr -> convertingUnsupported <- not isFSharp90Supported
| :? IReferenceExpr as ref when not ref.IsQualified ->
if ref.ShortName <> rootRefExpr.ShortName then () else
let symbol = ref.Reference.GetFcsSymbol()
Expand All @@ -304,8 +306,8 @@ type LambdaAnalyzer() =
})
if convertingUnsupported then null
//TODO: workaround for https://github.com/dotnet/fsharp/issues/16305
elif not (isLambdaArgOwnerSupported lambda false ValueNone) then null
else rootRefExpr
elif isFSharp90Supported || isLambdaArgOwnerSupported lambda false ValueNone then rootRefExpr
else null

let isApplicable (expr: IFSharpExpression) (pats: TreeNodeCollection<IFSharpPattern>) =
match expr with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ type FSharpRemoveRedundantParensFixBase(parensNode: IFSharpTreeNode, innerNode:
FSharpRemoveRedundantParensScopedFixingStrategy.Instance

abstract AddSpaceAfter: prevToken: ITokenNode -> bool
default _.AddSpaceAfter(prevToken: ITokenNode) = isIdentifierOrKeyword prevToken
default _.AddSpaceAfter(prevToken: ITokenNode) = shouldAddSpaceAfter prevToken

abstract AddSpaceBefore: nextToken: ITokenNode -> bool
default _.AddSpaceBefore(nextToken: ITokenNode) = isIdentifierOrKeyword nextToken
default _.AddSpaceBefore(nextToken: ITokenNode) = shouldAddSpaceBefore nextToken

override x.ExecutePsiTransaction _ =
use writeCookie = WriteLockCookie.Create(parensNode.IsPhysical())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes

open JetBrains.ReSharper.Plugins.FSharp.ProjectModel
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.Highlightings
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util.FSharpParensUtil
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Psi.ExtensionsAPI
open JetBrains.ReSharper.Psi.ExtensionsAPI.Tree
open JetBrains.ReSharper.Psi.Tree
open JetBrains.ReSharper.Resources.Shell

type ReplaceLambdaWithDotLambdaFix(warning: DotLambdaCanBeUsedWarning) =
Expand All @@ -14,7 +19,9 @@ type ReplaceLambdaWithDotLambdaFix(warning: DotLambdaCanBeUsedWarning) =
let lambda = warning.Lambda
let expr = warning.Lambda.Expression.IgnoreInnerParens()

override x.IsAvailable _ = isValid lambda && isValid expr && isContextWithoutWildPats expr
override x.IsAvailable _ =
isValid lambda && isValid expr &&
(FSharpLanguageLevel.isFSharp90Supported expr || isContextWithoutWildPats expr)

override x.Text = "Replace with shorthand lambda"

Expand All @@ -30,4 +37,16 @@ type ReplaceLambdaWithDotLambdaFix(warning: DotLambdaCanBeUsedWarning) =

let dotLambda = factory.CreateDotLambda()
dotLambda.SetExpression(expr) |> ignore
replace lambda dotLambda

let exprToReplace = lambda.IgnoreParentParens(includingBeginEndExpr = false)
let hasParens = exprToReplace :? IParenExpr

let dotLambda = ModificationUtil.ReplaceChild(exprToReplace, dotLambda)
let replaced =
if hasParens then addParensIfNeeded dotLambda else dotLambda
|> _.IgnoreParentParens()

if replaced :? IParenExpr then () else

if shouldAddSpaceAfter (replaced.GetPreviousToken()) then addNodeBefore replaced (Whitespace())
if shouldAddSpaceBefore (replaced.GetNextToken()) then addNodeAfter replaced (Whitespace())
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util.FSharpParensUtil

open System
open FSharp.Compiler.Syntax
open JetBrains.ReSharper.Plugins.FSharp.ProjectModel
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Parsing
Expand Down Expand Up @@ -269,8 +270,8 @@ let literalsRequiringParens =
let rec needsParensImpl (allowHighPrecedenceAppParens: unit -> bool) (context: IFSharpExpression) (expr: IFSharpExpression) =
if escapesTupleAppArg context expr then true else
if expr :? IParenOrBeginEndExpr then false
// TODO:
elif expr :? IDotLambdaExpr then true else

elif expr :? IDotLambdaExpr && not (FSharpLanguageLevel.isFSharp90Supported expr) then true else

let expr = expr.IgnoreInnerParens()
if isNull expr|| contextRequiresParens expr context then true else
Expand Down Expand Up @@ -447,3 +448,6 @@ let addParensIfNeeded (expr: IFSharpExpression) =
let context = expr.IgnoreParentParens(includingBeginEndExpr = false)
if context != expr || not (needsParens context expr) then expr else
addParens expr

let shouldAddSpaceAfter (prevToken: ITokenNode) = isIdentifierOrKeyword prevToken
let shouldAddSpaceBefore (nextToken: ITokenNode) = isIdentifierOrKeyword nextToken
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
open System
open System.Linq
open System.Linq.Expressions

// Available
ignore <| fun x -> x.ToString()
ignore <| fun x -> x . ToString()
ignore <| fun x -> (x).ToString()
ignore <| fun (x) -> x.ToString()
ignore <| fun x -> (x.ToString())
ignore <| fun x -> (x.ToString())
ignore <| fun x -> x.Prop[0]
ignore <| fun x -> x.Prop.[0]
ignore <| <@ fun x -> fun y -> y.ToString() @>
[1] |> (fun x -> x.ToString(fun x -> x.ToString())) |> ignore
[1] |> (fun x -> x.Equals(let x = 5 in x)) |> ignore
type A1 = member _.M() = fun x -> x.ToString()
let f (a, (b, c)) = fun x -> fun x -> x.ToString()
match 1 with _ -> fun x -> x.ToString()
let _ = let _ = 3 in fun x -> x.ToString()

type A = static member M(x: int -> string) = ()
A.M(fun x -> x.ToString())

ignore <| <@ fun x -> x.ToString() @>
ignore <| <@@ fun x -> x.ToString() @@>
[1] |> (fun x -> x.Select(_.ToString())) |> ignore
[1] |> _.Select(fun x -> x.ToString()) |> ignore
(fun _ -> fun x -> x.ToString()) |> ignore
(fun _ -> fun y -> fun x -> x.ToString()) |> ignore
(fun (a, (b, _)) -> fun y -> fun x -> x.ToString()) |> ignore
type A2 = member _.M _ = fun x -> x.ToString()
let g (a, (b, Some _)) = fun x -> fun x -> x.ToString()

type A with static member M(x: int, y: Func<int, string>) = ()
type A with static member M(x: string, y: Expression<Func<int, string>>) = ()
A.M(1, fun x -> x.ToString())
A.M("", fun x -> x.ToString())


// Not available
ignore <| fun x -> x.Equals(x)
ignore <| fun x -> x.ToString ()
ignore <| fun x -> y.ToString()
ignore <| fun x -> (x.ToString)()
[1] |> (fun x -> x[0]) |> ignore
[1] |> (fun x -> x.[0]) |> ignore
ignore <| fun x -> x.ToString()[0]
ignore <| fun (x, y) -> x.ToString()
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
open System
open System.Linq
open System.Linq.Expressions

// Available
ignore <| |fun x ->|(0) x.ToString()
ignore <| |fun x ->|(1) x . ToString()
ignore <| |fun x ->|(2) (x).ToString()
ignore <| |fun (x) ->|(3) x.ToString()
ignore <| |fun x ->|(4) (x.ToString())
ignore <| |fun x ->|(5) (x.ToString())
ignore <| |fun x ->|(6) x.Prop[0]
ignore <| |fun x ->|(7) x.Prop.[0]
ignore <| <@ fun x -> |fun y ->|(8) y.ToString() @>
[1] |> (|fun x ->|(9) x.ToString(|fun x ->|(10) x.ToString())) |> ignore
[1] |> (|fun x ->|(11) x.Equals(let x = 5 in x)) |> ignore
type A1 = member _.M() = |fun x ->|(12) x.ToString()
let f (a, (b, c)) = fun x -> |fun x ->|(13) x.ToString()
match 1 with _ -> |fun x ->|(14) x.ToString()
let _ = let _ = 3 in |fun x ->|(15) x.ToString()

type A = static member M(x: int -> string) = ()
A.M(|fun x ->|(16) x.ToString())

ignore <| <@ |fun x ->|(17) x.ToString() @>
ignore <| <@@ |fun x ->|(18) x.ToString() @@>
[1] |> (|fun x ->|(19) x.Select(_.ToString())) |> ignore
[1] |> _.Select(|fun x ->|(20) x.ToString()) |> ignore
(fun _ -> |fun x ->|(21) x.ToString()) |> ignore
(fun _ -> fun y -> |fun x ->|(22) x.ToString()) |> ignore
(fun (a, (b, _)) -> fun y -> |fun x ->|(23) x.ToString()) |> ignore
type A2 = member _.M _ = |fun x ->|(24) x.ToString()
let g (a, (b, Some _)) = fun x -> |fun x ->|(25) x.ToString()

type A with static member M(x: int, y: Func<int, string>) = ()
type A with static member M(x: string, y: Expression<Func<int, string>>) = ()
A.M(1, |fun x ->|(26) x.ToString())
A.M("", |fun x ->|(27) x.ToString())


// Not available
ignore <| fun x -> x.Equals(x)
ignore <| fun x -> x.ToString ()
ignore <| fun x -> y.ToString()
ignore <| fun x -> (x.ToString)()
[1] |> (fun x -> x[0]) |> ignore
[1] |> (fun x -> x.[0]) |> ignore
ignore <| fun x -> x.ToString()[0]
ignore <| fun (x, y) -> x.ToString()

---------------------------------------------------------
(0): ReSharper Dead Code: Shorthand lambda can be used
(1): ReSharper Dead Code: Shorthand lambda can be used
(2): ReSharper Dead Code: Shorthand lambda can be used
(3): ReSharper Dead Code: Shorthand lambda can be used
(4): ReSharper Dead Code: Shorthand lambda can be used
(5): ReSharper Dead Code: Shorthand lambda can be used
(6): ReSharper Dead Code: Shorthand lambda can be used
(7): ReSharper Dead Code: Shorthand lambda can be used
(8): ReSharper Dead Code: Shorthand lambda can be used
(9): ReSharper Dead Code: Shorthand lambda can be used
(10): ReSharper Dead Code: Shorthand lambda can be used
(11): ReSharper Dead Code: Shorthand lambda can be used
(12): ReSharper Dead Code: Shorthand lambda can be used
(13): ReSharper Dead Code: Shorthand lambda can be used
(14): ReSharper Dead Code: Shorthand lambda can be used
(15): ReSharper Dead Code: Shorthand lambda can be used
(16): ReSharper Dead Code: Shorthand lambda can be used
(17): ReSharper Dead Code: Shorthand lambda can be used
(18): ReSharper Dead Code: Shorthand lambda can be used
(19): ReSharper Dead Code: Shorthand lambda can be used
(20): ReSharper Dead Code: Shorthand lambda can be used
(21): ReSharper Dead Code: Shorthand lambda can be used
(22): ReSharper Dead Code: Shorthand lambda can be used
(23): ReSharper Dead Code: Shorthand lambda can be used
(24): ReSharper Dead Code: Shorthand lambda can be used
(25): ReSharper Dead Code: Shorthand lambda can be used
(26): ReSharper Dead Code: Shorthand lambda can be used
(27): ReSharper Dead Code: Shorthand lambda can be used
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
(_.ToString())
(_.ToString)
id(_.ToString())
id (_.ToString())
M(_.ToString())
M (_.ToString())
f 1 (_.ToString) 1
f 1(_.ToString)1
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
(_.ToString())
(_.ToString)
id(_.ToString())
id (_.ToString())
M(_.ToString())
M (_.ToString())
f 1 (_.ToString) 1
f 1(_.ToString)1

---------------------------------------------------------
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(_.ToString())
(_.ToString)
id(_.ToString())
id (_.ToString())
M(_.ToString())
M (_.ToString())
f 1 (_.ToString) 1
f 1(_.ToString)1
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
AllowHighPrecedenceAppParens = True
|(|(0)_.ToString()|)|(1)
|(|(2)_.ToString|)|(3)
id(_.ToString())
id |(|(4)_.ToString()|)|(5)
M(_.ToString())
M |(|(6)_.ToString()|)|(7)
f 1 |(|(8)_.ToString|)|(9) 1
f 1(_.ToString)1

---------------------------------------------------------
(0): ReSharper Dead Code: Redundant parentheses
(1):<secondary> ReSharper Dead Code: Redundant parentheses
(2): ReSharper Dead Code: Redundant parentheses
(3):<secondary> ReSharper Dead Code: Redundant parentheses
(4): ReSharper Dead Code: Redundant parentheses
(5):<secondary> ReSharper Dead Code: Redundant parentheses
(6): ReSharper Dead Code: Redundant parentheses
(7):<secondary> ReSharper Dead Code: Redundant parentheses
(8): ReSharper Dead Code: Redundant parentheses
(9):<secondary> ReSharper Dead Code: Redundant parentheses

================
AllowHighPrecedenceAppParens = False
|(|(0)_.ToString()|)|(1)
|(|(2)_.ToString|)|(3)
id|(|(4)_.ToString()|)|(5)
id |(|(6)_.ToString()|)|(7)
M|(|(8)_.ToString()|)|(9)
M |(|(10)_.ToString()|)|(11)
f 1 |(|(12)_.ToString|)|(13) 1
f 1|(|(14)_.ToString|)|(15)1

---------------------------------------------------------
(0): ReSharper Dead Code: Redundant parentheses
(1):<secondary> ReSharper Dead Code: Redundant parentheses
(2): ReSharper Dead Code: Redundant parentheses
(3):<secondary> ReSharper Dead Code: Redundant parentheses
(4): ReSharper Dead Code: Redundant parentheses
(5):<secondary> ReSharper Dead Code: Redundant parentheses
(6): ReSharper Dead Code: Redundant parentheses
(7):<secondary> ReSharper Dead Code: Redundant parentheses
(8): ReSharper Dead Code: Redundant parentheses
(9):<secondary> ReSharper Dead Code: Redundant parentheses
(10): ReSharper Dead Code: Redundant parentheses
(11):<secondary> ReSharper Dead Code: Redundant parentheses
(12): ReSharper Dead Code: Redundant parentheses
(13):<secondary> ReSharper Dead Code: Redundant parentheses
(14): ReSharper Dead Code: Redundant parentheses
(15):<secondary> ReSharper Dead Code: Redundant parentheses
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
open System
open System.Linq
open System.Linq.Expressions

// Available
ignore <| fun x{caret} -> x.ToString()
ignore <| fun x -> x . ToString()
ignore <| fun x -> (x).ToString()
ignore <| fun (x) -> x.ToString()
ignore <| fun x -> (x.ToString())
ignore <| fun x -> (x.ToString())
ignore <| fun x -> x.Prop[0]
ignore <| fun x -> x.Prop.[0]
ignore <| <@ fun x -> fun y -> y.ToString() @>
[1] |> (fun x -> x.ToString(fun x -> x.ToString())) |> ignore
[1] |> (fun x -> x.Equals(let x = 5 in x)) |> ignore
type A1 = member _.M() = fun x -> x.ToString()
let f (a, (b, c)) = fun x -> fun x -> x.ToString()
match 1 with _ -> fun x -> x.ToString()
let _ = let _ = 3 in fun x -> x.ToString()

type A = static member M(x: int -> string) = ()
A.M(fun x -> x.ToString())

ignore <| <@ fun x -> x.ToString() @>
ignore <| <@@ fun x -> x.ToString() @@>
[1] |> (fun x -> x.Select(_.ToString())) |> ignore
[1] |> _.Select(fun x -> x.ToString()) |> ignore
(fun _ -> fun x -> x.ToString()) |> ignore
(fun _ -> fun y -> fun x -> x.ToString()) |> ignore
(fun (a, (b, _)) -> fun y -> fun x -> x.ToString()) |> ignore
type A2 = member _.M _ = fun x -> x.ToString()
let g (a, (b, Some _)) = fun x -> fun x -> x.ToString()

type A with static member M(x: int, y: Func<int, string>) = ()
type A with static member M(x: string, y: Expression<Func<int, string>>) = ()
A.M(1, fun x -> x.ToString())
A.M("", fun x -> x.ToString())


// Not available
ignore <| fun x -> x.Equals(x)
ignore <| fun x -> x.ToString ()
ignore <| fun x -> y.ToString()
ignore <| fun x -> (x.ToString)()
[1] |> (fun x -> x[0]) |> ignore
[1] |> (fun x -> x.[0]) |> ignore
ignore <| fun x -> x.ToString()[0]
ignore <| fun (x, y) -> x.ToString()
Loading
Loading