From a3ad0cc4afa0c32854ee8da24bacf465a8aee366 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Thu, 19 Sep 2024 20:23:42 +0300 Subject: [PATCH 1/2] wip --- .../src/Analyzers/LambdaAnalyzer.fs | 8 +- .../src/QuickFixes/RemoveRedundantParens.fs | 4 +- .../ReplaceLambdaWithDotLambdaFix.fs | 23 +++++- .../src/Util/FSharpParensUtil.fs | 8 +- .../Dot lambda - Availability - F# 9.fs | 49 ++++++++++++ .../Dot lambda - Availability - F# 9.fs.gold | 79 +++++++++++++++++++ .../redundantParens/expr/Dot lambda 01.fs | 6 ++ .../expr/Dot lambda 01.fs.gold | 6 ++ .../expr/Dot lambda 02 - F# 9.fs | 8 ++ .../expr/Dot lambda 02 - F# 9.fs.gold | 50 ++++++++++++ .../File scoped - F# 9.fs | 49 ++++++++++++ .../File scoped - F# 9.fs.gold | 49 ++++++++++++ .../File scoped - Overlap 01 - F# 9.fs | 1 + .../File scoped - Overlap 01 - F# 9.fs.gold | 1 + .../File scoped - Overlap 02 - F# 9.fs | 1 + .../File scoped - Overlap 02 - F# 9.fs.gold | 1 + .../File scoped - Remove parens.fs | 10 +++ .../File scoped - Remove parens.fs.gold | 10 +++ .../src/Daemon/LambdaAnalyzerTest.fs | 5 ++ .../src/Daemon/RedundantParenExprTest.fs | 4 + .../QuickFixes/ReplaceWithDotLambdaTest.fs | 14 ++++ 21 files changed, 377 insertions(+), 9 deletions(-) create mode 100644 ReSharper.FSharp/test/data/features/daemon/lambdaAnalyzer/Dot lambda - Availability - F# 9.fs create mode 100644 ReSharper.FSharp/test/data/features/daemon/lambdaAnalyzer/Dot lambda - Availability - F# 9.fs.gold create mode 100644 ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 02 - F# 9.fs create mode 100644 ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 02 - F# 9.fs.gold create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - F# 9.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - F# 9.fs.gold create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 01 - F# 9.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 01 - F# 9.fs.gold create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 02 - F# 9.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 02 - F# 9.fs.gold create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Remove parens.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Remove parens.fs.gold diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs index 3ae4b4aa18..8f73ee9957 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs @@ -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 @@ -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 && not (isContextWithoutWildPats expr) then null else let patSymbol = pat.GetFcsSymbol() let mutable convertingUnsupported = false @@ -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() @@ -304,7 +306,7 @@ type LambdaAnalyzer() = }) if convertingUnsupported then null //TODO: workaround for https://github.com/dotnet/fsharp/issues/16305 - elif not (isLambdaArgOwnerSupported lambda false ValueNone) then null + elif not isFSharp90Supported && not (isLambdaArgOwnerSupported lambda false ValueNone) then null else rootRefExpr let isApplicable (expr: IFSharpExpression) (pats: TreeNodeCollection) = diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/src/QuickFixes/RemoveRedundantParens.fs b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/src/QuickFixes/RemoveRedundantParens.fs index 08eac3b179..c23674ff58 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/src/QuickFixes/RemoveRedundantParens.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/src/QuickFixes/RemoveRedundantParens.fs @@ -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()) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/src/QuickFixes/ReplaceLambdaWithDotLambdaFix.fs b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/src/QuickFixes/ReplaceLambdaWithDotLambdaFix.fs index d23b123e8f..2a8f71835a 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/src/QuickFixes/ReplaceLambdaWithDotLambdaFix.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/src/QuickFixes/ReplaceLambdaWithDotLambdaFix.fs @@ -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) = @@ -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" @@ -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()) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Services/src/Util/FSharpParensUtil.fs b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Services/src/Util/FSharpParensUtil.fs index 3b240f4642..230a252a73 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Services/src/Util/FSharpParensUtil.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Services/src/Util/FSharpParensUtil.fs @@ -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 @@ -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 @@ -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 diff --git a/ReSharper.FSharp/test/data/features/daemon/lambdaAnalyzer/Dot lambda - Availability - F# 9.fs b/ReSharper.FSharp/test/data/features/daemon/lambdaAnalyzer/Dot lambda - Availability - F# 9.fs new file mode 100644 index 0000000000..cead5b7846 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/daemon/lambdaAnalyzer/Dot lambda - Availability - F# 9.fs @@ -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) = () +type A with static member M(x: string, y: Expression>) = () +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() diff --git a/ReSharper.FSharp/test/data/features/daemon/lambdaAnalyzer/Dot lambda - Availability - F# 9.fs.gold b/ReSharper.FSharp/test/data/features/daemon/lambdaAnalyzer/Dot lambda - Availability - F# 9.fs.gold new file mode 100644 index 0000000000..448d0fe9b5 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/daemon/lambdaAnalyzer/Dot lambda - Availability - F# 9.fs.gold @@ -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) = () +type A with static member M(x: string, y: Expression>) = () +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 diff --git a/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 01.fs b/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 01.fs index 2496d89f40..959d0d542b 100644 --- a/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 01.fs +++ b/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 01.fs @@ -1,2 +1,8 @@ (_.ToString()) (_.ToString) +id(_.ToString()) +id (_.ToString()) +M(_.ToString()) +M (_.ToString()) +f 1 (_.ToString) 1 +f 1(_.ToString)1 diff --git a/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 01.fs.gold b/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 01.fs.gold index 571f1c5e78..a8e0f2e34d 100644 --- a/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 01.fs.gold +++ b/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 01.fs.gold @@ -1,4 +1,10 @@ (_.ToString()) (_.ToString) +id(_.ToString()) +id (_.ToString()) +M(_.ToString()) +M (_.ToString()) +f 1 (_.ToString) 1 +f 1(_.ToString)1 --------------------------------------------------------- diff --git a/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 02 - F# 9.fs b/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 02 - F# 9.fs new file mode 100644 index 0000000000..959d0d542b --- /dev/null +++ b/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 02 - F# 9.fs @@ -0,0 +1,8 @@ +(_.ToString()) +(_.ToString) +id(_.ToString()) +id (_.ToString()) +M(_.ToString()) +M (_.ToString()) +f 1 (_.ToString) 1 +f 1(_.ToString)1 diff --git a/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 02 - F# 9.fs.gold b/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 02 - F# 9.fs.gold new file mode 100644 index 0000000000..f4ef3191fc --- /dev/null +++ b/ReSharper.FSharp/test/data/features/daemon/redundantParens/expr/Dot lambda 02 - F# 9.fs.gold @@ -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): ReSharper Dead Code: Redundant parentheses +(2): ReSharper Dead Code: Redundant parentheses +(3): ReSharper Dead Code: Redundant parentheses +(4): ReSharper Dead Code: Redundant parentheses +(5): ReSharper Dead Code: Redundant parentheses +(6): ReSharper Dead Code: Redundant parentheses +(7): ReSharper Dead Code: Redundant parentheses +(8): ReSharper Dead Code: Redundant parentheses +(9): 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): ReSharper Dead Code: Redundant parentheses +(2): ReSharper Dead Code: Redundant parentheses +(3): ReSharper Dead Code: Redundant parentheses +(4): ReSharper Dead Code: Redundant parentheses +(5): ReSharper Dead Code: Redundant parentheses +(6): ReSharper Dead Code: Redundant parentheses +(7): ReSharper Dead Code: Redundant parentheses +(8): ReSharper Dead Code: Redundant parentheses +(9): ReSharper Dead Code: Redundant parentheses +(10): ReSharper Dead Code: Redundant parentheses +(11): ReSharper Dead Code: Redundant parentheses +(12): ReSharper Dead Code: Redundant parentheses +(13): ReSharper Dead Code: Redundant parentheses +(14): ReSharper Dead Code: Redundant parentheses +(15): ReSharper Dead Code: Redundant parentheses diff --git a/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - F# 9.fs b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - F# 9.fs new file mode 100644 index 0000000000..d95a373d95 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - F# 9.fs @@ -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) = () +type A with static member M(x: string, y: Expression>) = () +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() diff --git a/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - F# 9.fs.gold b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - F# 9.fs.gold new file mode 100644 index 0000000000..20fa2a9ea5 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - F# 9.fs.gold @@ -0,0 +1,49 @@ +open System +open System.Linq +open System.Linq.Expressions + +// Available +ignore <| _{caret}.ToString() +ignore <| _.ToString() +ignore <| _.ToString() +ignore <| _.ToString() +ignore <| _.ToString() +ignore <| _.ToString() +ignore <| _.Prop[0] +ignore <| _.Prop.[0] +ignore <| <@ fun x -> _.ToString() @> +[1] |> _.ToString(_.ToString()) |> ignore +[1] |> _.Equals(let x = 5 in x) |> ignore +type A1 = member _.M() = _.ToString() +let f (a, (b, c)) = fun x -> _.ToString() +match 1 with _ -> _.ToString() +let _ = let _ = 3 in _.ToString() + +type A = static member M(x: int -> string) = () +A.M(_.ToString()) + +ignore <| <@ _.ToString() @> +ignore <| <@@ _.ToString() @@> +[1] |> _.Select(_.ToString()) |> ignore +[1] |> _.Select(_.ToString()) |> ignore +(fun _ -> _.ToString()) |> ignore +(fun _ -> fun y -> _.ToString()) |> ignore +(fun (a, (b, _)) -> fun y -> _.ToString()) |> ignore +type A2 = member _.M _ = _.ToString() +let g (a, (b, Some _)) = fun x -> _.ToString() + +type A with static member M(x: int, y: Func) = () +type A with static member M(x: string, y: Expression>) = () +A.M(1, _.ToString()) +A.M("", _.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() diff --git a/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 01 - F# 9.fs b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 01 - F# 9.fs new file mode 100644 index 0000000000..fbcc8d70b6 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 01 - F# 9.fs @@ -0,0 +1 @@ +fun x{caret} -> x.ToString(fun x -> x.ToString()) diff --git a/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 01 - F# 9.fs.gold b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 01 - F# 9.fs.gold new file mode 100644 index 0000000000..0158f90e73 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 01 - F# 9.fs.gold @@ -0,0 +1 @@ +_{caret}.ToString(_.ToString()) diff --git a/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 02 - F# 9.fs b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 02 - F# 9.fs new file mode 100644 index 0000000000..7642b546c1 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 02 - F# 9.fs @@ -0,0 +1 @@ +fun x -> x.ToString(fun{caret} x -> x.ToString()) diff --git a/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 02 - F# 9.fs.gold b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 02 - F# 9.fs.gold new file mode 100644 index 0000000000..488ff5310a --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Overlap 02 - F# 9.fs.gold @@ -0,0 +1 @@ +_.ToString(_{caret}.ToString()) diff --git a/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Remove parens.fs b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Remove parens.fs new file mode 100644 index 0000000000..c5c47a3820 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Remove parens.fs @@ -0,0 +1,10 @@ +module Test + +f(fun x{caret} -> x.ToString()) +f x(fun x -> x.ToString())x + +open System.Linq +[1;2;3] |> List.map (fun x -> x.ToString()) +[1;2;3] |> List.map(fun x -> x.ToString()) +[1;2;3] |> List.map (fun x -> x.ToString())|> List.map id +[1;2;3].Select(fun x -> x.GetHashCode()).Where(fun x -> x.Equals) diff --git a/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Remove parens.fs.gold b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Remove parens.fs.gold new file mode 100644 index 0000000000..cd1a7a372f --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/useDotLambdaSyntaxFix/File scoped - Remove parens.fs.gold @@ -0,0 +1,10 @@ +module Test + +f(_{caret}.ToString()) +f x(_.ToString())x + +open System.Linq +[1;2;3] |> List.map _.ToString() +[1;2;3] |> List.map(_.ToString()) +[1;2;3] |> List.map _.ToString() |> List.map id +[1;2;3].Select(_.GetHashCode()).Where(_.Equals) diff --git a/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/Daemon/LambdaAnalyzerTest.fs b/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/Daemon/LambdaAnalyzerTest.fs index 7a12a3051a..44009f53ee 100644 --- a/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/Daemon/LambdaAnalyzerTest.fs +++ b/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/Daemon/LambdaAnalyzerTest.fs @@ -42,5 +42,10 @@ type LambdaAnalyzerTest() = [] member x.``Forced calculations``() = x.DoNamedTest() [] member x.``Used names - Nested scope``() = x.DoNamedTest() [] member x.``Optional parameters``() = x.DoNamedTest() + + [] [] member x.``Dot lambda - Availability``() = x.DoNamedTest() + [] + [] member x.``Dot lambda - Availability - F# 9``() = x.DoNamedTest() + [] member x.``Dot lambda - Availability - Modules``() = x.DoNamedTest() diff --git a/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/Daemon/RedundantParenExprTest.fs b/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/Daemon/RedundantParenExprTest.fs index 369fa987b7..e2b24e10f4 100644 --- a/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/Daemon/RedundantParenExprTest.fs +++ b/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/Daemon/RedundantParenExprTest.fs @@ -63,8 +63,12 @@ type RedundantParenExprTest() = [] member x.``Binary - Op deindent 01``() = x.DoNamedTest() [] member x.``Binary - Typed 01``() = x.DoNamedTest() + [] [] member x.``Dot lambda 01``() = x.DoNamedTest() + [] + [] member x.``Dot lambda 02 - F# 9``() = x.DoNamedTest() + [] member x.``Dynamic 01``() = x.DoNamedTest() [] member x.``Dynamic 02``() = x.DoNamedTest() [] member x.``Dynamic 03``() = x.DoNamedTest() diff --git a/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/QuickFixes/ReplaceWithDotLambdaTest.fs b/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/QuickFixes/ReplaceWithDotLambdaTest.fs index dddd987189..d4c5921c4b 100644 --- a/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/QuickFixes/ReplaceWithDotLambdaTest.fs +++ b/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/src/QuickFixes/ReplaceWithDotLambdaTest.fs @@ -1,6 +1,7 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Tests.Intentions.QuickFixes open JetBrains.ReSharper.FeaturesTestFramework.Intentions +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.Tests @@ -12,10 +13,23 @@ type ReplaceLambdaWithDotLambdaTest() = override x.RelativeTestDataPath = "features/quickFixes/useDotLambdaSyntaxFix" + [] [] member x.``File scoped`` () = x.DoNamedTest() + [] [] member x.``File scoped - Overlap 01`` () = x.DoNamedTest() + [] [] member x.``File scoped - Overlap 02`` () = x.DoNamedTest() + [] + [] member x.``File scoped - F# 9`` () = x.DoNamedTest() + [] + [] member x.``File scoped - Overlap 01 - F# 9`` () = x.DoNamedTest() + [] + [] member x.``File scoped - Overlap 02 - F# 9`` () = x.DoNamedTest() + + [] + [] member x.``File scoped - Remove parens`` () = x.DoNamedTest() + [] type ReplaceLambdaWithDotLambdaAvailabilityTest() = From ffb469c94a64d741dcc5c9db6df55f928419e1c3 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Thu, 19 Sep 2024 20:40:27 +0300 Subject: [PATCH 2/2] simplify --- .../FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs index 8f73ee9957..b53c764b3a 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Analyzers/LambdaAnalyzer.fs @@ -286,7 +286,7 @@ type LambdaAnalyzer() = let rootRefExpr = getRootRefExpr expr if isNull rootRefExpr || rootRefExpr.ShortName <> pat.SourceName || - not isFSharp90Supported && not (isContextWithoutWildPats expr) then null else + not (isFSharp90Supported || isContextWithoutWildPats expr) then null else let patSymbol = pat.GetFcsSymbol() let mutable convertingUnsupported = false @@ -306,8 +306,8 @@ type LambdaAnalyzer() = }) if convertingUnsupported then null //TODO: workaround for https://github.com/dotnet/fsharp/issues/16305 - elif not isFSharp90Supported && 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) = match expr with