From cb65a83800cbc4e7709e6a24aaeae4f72f844096 Mon Sep 17 00:00:00 2001 From: Matt Constable Date: Sat, 1 Aug 2020 08:57:11 +0100 Subject: [PATCH 1/3] Generalize missing records fix and add tests --- .../ReSharper.FSharp.sln.DotSettings.user | 8 ++++++ .../FSharp.Psi/src/Impl/Tree/RecordExpr.cs | 28 ++++++++++++++----- .../Elif statement.fs | 9 ++++++ .../Elif statement.fs.gold | 10 +++++++ .../Function lambda.fs | 5 ++++ .../Function statement.fs | 3 ++ .../Function statement.fs.gold | 4 +++ .../If statement in function.fs | 11 ++++++++ .../If statement in function.fs.gold | 12 ++++++++ .../Match statement in function.fs | 7 +++++ .../Match statement in function.fs.gold | 8 ++++++ .../GenerateMissingRecordFieldsTest.fs | 4 +++ 12 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings.user create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Elif statement.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Elif statement.fs.gold create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function lambda.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function statement.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function statement.fs.gold create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/If statement in function.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/If statement in function.fs.gold create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Match statement in function.fs create mode 100644 ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Match statement in function.fs.gold diff --git a/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings.user b/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings.user new file mode 100644 index 0000000000..79d661288c --- /dev/null +++ b/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings.user @@ -0,0 +1,8 @@ + + <SessionState ContinuousTestingMode="0" Name="GenerateMissingRecordFieldsTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <TestAncestor> + <TestId>NUnit3x::AF85AA16-C863-42F2-AE7E-44C3E36F8883::.NETFramework,Version=v4.6.1::JetBrains.ReSharper.Plugins.FSharp.Tests.Features.GenerateMissingRecordFieldsTest</TestId> + <TestId>NUnit3x::AF85AA16-C863-42F2-AE7E-44C3E36F8883::.NETFramework,Version=v4.6.1::JetBrains.ReSharper.Plugins.FSharp.Tests.Features.GenerateMissingRecordFieldsAvailabilityTest</TestId> + <TestId>NUnit3x::AF85AA16-C863-42F2-AE7E-44C3E36F8883::.NETFramework,Version=v4.6.1::JetBrains.ReSharper.Plugins.FSharp.Tests.Features.FSharpParserTest.Attribute 01</TestId> + </TestAncestor> +</SessionState> \ No newline at end of file diff --git a/ReSharper.FSharp/src/FSharp.Psi/src/Impl/Tree/RecordExpr.cs b/ReSharper.FSharp/src/FSharp.Psi/src/Impl/Tree/RecordExpr.cs index 96ed7ddf4a..4267eec7ea 100644 --- a/ReSharper.FSharp/src/FSharp.Psi/src/Impl/Tree/RecordExpr.cs +++ b/ReSharper.FSharp/src/FSharp.Psi/src/Impl/Tree/RecordExpr.cs @@ -60,13 +60,8 @@ public override FSharpSymbol GetFSharpSymbol() if (symbolUse?.Symbol is FSharpField field) return field.DeclaringEntity?.Value; - // todo: cover other contexts - var sequentialExpr = SequentialExprNavigator.GetByExpression(RecordExpr.IgnoreParentParens()); - if (sequentialExpr != null && sequentialExpr.Expressions.Last() != RecordExpr) - return null; - var exprToGetBy = sequentialExpr ?? RecordExpr.IgnoreParentParens(); - - var binding = BindingNavigator.GetByExpression(exprToGetBy); + var functionExpr = GetFunctionExpression(RecordExpr); + var binding = BindingNavigator.GetByExpression(functionExpr); if (binding == null || !(binding.HeadPattern is INamedPat namedPat)) return null; @@ -79,6 +74,25 @@ public override FSharpSymbol GetFSharpSymbol() return entity.IsFSharpRecord ? entity : null; } + private static IFSharpExpression GetFunctionExpression(IFSharpExpression expression) + { + IFSharpExpression currentExpression = IfExprNavigator.GetByThenExpr(expression); + currentExpression ??= IfExprNavigator.GetByElseExpr(expression); + var matchClause = MatchClauseNavigator.GetByExpression(expression); + currentExpression = matchClause == null ? currentExpression : MatchExprNavigator.GetByClause(matchClause); + currentExpression ??= MatchLambdaExprNavigator.GetByClause(matchClause); + + var sequentialExpr = SequentialExprNavigator.GetByExpression(expression); + currentExpression ??= sequentialExpr; + if (sequentialExpr != null && sequentialExpr.Expressions.Last() != expression) + return null; + + if (currentExpression == null) + return expression; + + return GetFunctionExpression(currentExpression); + } + public override string GetName() => SharedImplUtil.MISSING_DECLARATION_NAME; diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Elif statement.fs b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Elif statement.fs new file mode 100644 index 0000000000..10a26e5ef0 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Elif statement.fs @@ -0,0 +1,9 @@ +type R = { A: int; B: int } + +let r (shouldFail : bool) : R = + if shouldFail then + failwith "" + elif true then + {}{caret} + else + failwith "" \ No newline at end of file diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Elif statement.fs.gold b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Elif statement.fs.gold new file mode 100644 index 0000000000..73378c8f78 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Elif statement.fs.gold @@ -0,0 +1,10 @@ +type R = { A: int; B: int } + +let r (shouldFail : bool) : R = + if shouldFail then + failwith "" + elif true then + { A = {selstart}failwith "todo"{selend} + B = failwith "todo" } + else + failwith "" \ No newline at end of file diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function lambda.fs b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function lambda.fs new file mode 100644 index 0000000000..735571663d --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function lambda.fs @@ -0,0 +1,5 @@ +type R = { A: int; B: int } +module foo = + let r (input : int option) : (R option) = + input + |> Option.map(fun x -> {}) \ No newline at end of file diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function statement.fs b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function statement.fs new file mode 100644 index 0000000000..8a14dc5544 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function statement.fs @@ -0,0 +1,3 @@ +type R = { A: int; B: int } +module foo = + let r : (string option -> R) = function | Some _ -> {}{caret} | None -> {} \ No newline at end of file diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function statement.fs.gold b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function statement.fs.gold new file mode 100644 index 0000000000..de215fc2a4 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Function statement.fs.gold @@ -0,0 +1,4 @@ +type R = { A: int; B: int } +module foo = + let r : (string option -> R) = function | Some _ -> { A = {selstart}failwith "todo"{selend} + B = failwith "todo" } | None -> {} \ No newline at end of file diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/If statement in function.fs b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/If statement in function.fs new file mode 100644 index 0000000000..921392fadc --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/If statement in function.fs @@ -0,0 +1,11 @@ +type R = { A: int; B: int } + +let r (shouldFail : bool) : R = + () + if shouldFail then + failwith "" + else + if true then + {}{caret} + else + failwith "" diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/If statement in function.fs.gold b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/If statement in function.fs.gold new file mode 100644 index 0000000000..736b62d5c3 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/If statement in function.fs.gold @@ -0,0 +1,12 @@ +type R = { A: int; B: int } + +let r (shouldFail : bool) : R = + () + if shouldFail then + failwith "" + else + if true then + { A = {selstart}failwith "todo"{selend} + B = failwith "todo" } + else + failwith "" diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Match statement in function.fs b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Match statement in function.fs new file mode 100644 index 0000000000..059f833044 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Match statement in function.fs @@ -0,0 +1,7 @@ +type R = { A: int; B: int } + +let r (someOption : option int) : R = + () + match someOption with + | Some value -> {A = value; B = value} + | None -> {}{caret} \ No newline at end of file diff --git a/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Match statement in function.fs.gold b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Match statement in function.fs.gold new file mode 100644 index 0000000000..d3769f6ea3 --- /dev/null +++ b/ReSharper.FSharp/test/data/features/quickFixes/generateMissingRecordFields/Match statement in function.fs.gold @@ -0,0 +1,8 @@ +type R = { A: int; B: int } + +let r (someOption : option int) : R = + () + match someOption with + | Some value -> {A = value; B = value} + | None -> { A = {selstart}failwith "todo"{selend} + B = failwith "todo" } \ No newline at end of file diff --git a/ReSharper.FSharp/test/src/FSharp.Tests/QuickFixes/GenerateMissingRecordFieldsTest.fs b/ReSharper.FSharp/test/src/FSharp.Tests/QuickFixes/GenerateMissingRecordFieldsTest.fs index 3b4e3b51c0..3d90d434a8 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests/QuickFixes/GenerateMissingRecordFieldsTest.fs +++ b/ReSharper.FSharp/test/src/FSharp.Tests/QuickFixes/GenerateMissingRecordFieldsTest.fs @@ -26,6 +26,10 @@ type GenerateMissingRecordFieldsTest() = [] member x.``Multiline 02``() = x.DoNamedTest() [] member x.``Empty function``() = x.DoNamedTest() + [] member x.``Function statement``() = x.DoNamedTest() + [] member x.``If statement in function``() = x.DoNamedTest() + [] member x.``Elif statement``() = x.DoNamedTest() + [] member x.``Match statement in function``() = x.DoNamedTest() [] type GenerateMissingRecordFieldsAvailabilityTest() = From 3388457c7358429beb3251631fd2a53e7d1a4689 Mon Sep 17 00:00:00 2001 From: Matt Constable Date: Sat, 1 Aug 2020 08:58:50 +0100 Subject: [PATCH 2/3] Remove DotSettings --- .../ReSharper.FSharp.sln.DotSettings | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings diff --git a/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings b/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings deleted file mode 100644 index ae1989e8ad..0000000000 --- a/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings +++ /dev/null @@ -1,26 +0,0 @@ - - False - - 2 - True - - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - From 649777b1c2f5488a77233183b563cb12b980afae Mon Sep 17 00:00:00 2001 From: Matthew Constable Date: Tue, 4 Aug 2020 21:23:19 +0100 Subject: [PATCH 3/3] Fix up DotSettings --- .../ReSharper.FSharp.sln.DotSettings | 26 +++++++++++++++++++ .../ReSharper.FSharp.sln.DotSettings.user | 8 ------ 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings delete mode 100644 ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings.user diff --git a/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings b/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings new file mode 100644 index 0000000000..ae1989e8ad --- /dev/null +++ b/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings @@ -0,0 +1,26 @@ + + False + + 2 + True + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + diff --git a/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings.user b/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings.user deleted file mode 100644 index 79d661288c..0000000000 --- a/ReSharper.FSharp/ReSharper.FSharp.sln.DotSettings.user +++ /dev/null @@ -1,8 +0,0 @@ - - <SessionState ContinuousTestingMode="0" Name="GenerateMissingRecordFieldsTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>NUnit3x::AF85AA16-C863-42F2-AE7E-44C3E36F8883::.NETFramework,Version=v4.6.1::JetBrains.ReSharper.Plugins.FSharp.Tests.Features.GenerateMissingRecordFieldsTest</TestId> - <TestId>NUnit3x::AF85AA16-C863-42F2-AE7E-44C3E36F8883::.NETFramework,Version=v4.6.1::JetBrains.ReSharper.Plugins.FSharp.Tests.Features.GenerateMissingRecordFieldsAvailabilityTest</TestId> - <TestId>NUnit3x::AF85AA16-C863-42F2-AE7E-44C3E36F8883::.NETFramework,Version=v4.6.1::JetBrains.ReSharper.Plugins.FSharp.Tests.Features.FSharpParserTest.Attribute 01</TestId> - </TestAncestor> -</SessionState> \ No newline at end of file