From 9733ae1e419c90bf7bd5e960f2dea7da52e589f3 Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 11 Jul 2023 14:25:50 +0200 Subject: [PATCH 1/3] Try restore record field completion. --- src/Compiler/Checking/NameResolution.fs | 2 +- tests/service/EditorTests.fs | 160 ++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index c2a7d50138e..b01e074229c 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -4887,7 +4887,7 @@ and ResolvePartialLongIdentToClassOrRecdFieldsImpl (ncenv: NameResolver) (nenv: let qualifiedFields = match rest with - | [] when not fieldsOnly -> + | [] -> // get record types accessible in given nenv let tycons = LookupTypeNameInEnvNoArity OpenQualified id nenv tycons diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 53d7e01d501..57eb82347e3 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -1926,3 +1926,163 @@ do let x = 1 in () data.MainDescription |> Array.map (fun text -> text.Text) |> String.concat "" |> shouldEqual "val x: int" | elements -> failwith $"Tooltip elements: {elements}" +let hasRecordField (fieldName:string) (symbolUses: FSharpSymbolUse list) = + symbolUses + |> List.exists (fun symbolUse -> + match symbolUse.Symbol with + | :? FSharpField as field -> field.DisplayName = fieldName + | _ -> false + ) + |> fun exists -> Assert.True(exists, $"Field {fieldName} not found.") + +let hasRecordType (recordTypeName: string) (symbolUses: FSharpSymbolUse list) = + symbolUses + |> List.exists (fun symbolUse -> + match symbolUse.Symbol with + | :? FSharpEntity as recordType -> recordType.IsFSharpRecord && recordType.DisplayName = recordTypeName + | _ -> false + ) + |> fun exists -> Assert.True(exists, $"Record type {recordTypeName} not found.") + +[] +let ``Record fields are completed via type name usage`` () = + let parseResults, checkResults = + getParseAndCheckResults """ +type Entry = + { + Idx: int + FileName: string + /// Own deps + DependencyCount: int + /// Being depended on + DependentCount: int + LineCount: int + } + +let x = + { + Entry. + } +""" + + let declarations = + checkResults.GetDeclarationListSymbols( + Some parseResults, + 14, + " Entry.", + { + EndColumn = 13 + LastDotPos = Some 13 + PartialIdent = "" + QualifyingIdents = [ "Entry" ] + }, + fun _ -> List.empty + ) + |> List.concat + + hasRecordField "Idx" declarations + hasRecordField "FileName" declarations + hasRecordField "DependentCount" declarations + hasRecordField "LineCount" declarations + +[] +let ``Record fields and types are completed via type name usage`` () = + let parseResults, checkResults = + getParseAndCheckResults """ +module Module1 = + type R1 = + { Field1: int } + + type R2 = + { Field2: int } + +module Module2 = + + { Module1. } +""" + + let declarations = + checkResults.GetDeclarationListSymbols( + Some parseResults, + 11, + " { Module1. }", + { + EndColumn = 13 + LastDotPos = Some 13 + PartialIdent = "" + QualifyingIdents = [ "Module1" ] + }, + fun _ -> List.empty + ) + |> List.concat + + hasRecordField "Field1" declarations + hasRecordField "Field2" declarations + hasRecordType "R1" declarations + hasRecordType "R2" declarations + +[] +let ``Record fields are completed via type name usage with open statement`` () = + let parseResults, checkResults = + getParseAndCheckResults """ +module Module1 = + type R1 = + { Field1: int } + + type R2 = + { Field2: int } + +module Module2 = + open Module1 + + { R1. } +""" + + let declarations = + checkResults.GetDeclarationListSymbols( + Some parseResults, + 12, + " { R1. }", + { + EndColumn = 8 + LastDotPos = Some 8 + PartialIdent = "" + QualifyingIdents = [ "R1" ] + }, + fun _ -> List.empty + ) + |> List.concat + + hasRecordField "Field1" declarations + +[] +let ``Record fields are completed via type name with module usage`` () = + let parseResults, checkResults = + getParseAndCheckResults """ +module Module1 = + type R1 = + { Field1: int } + + type R2 = + { Field2: int } + +module Module2 = + { Module1.R1. } +""" + + let declarations = + checkResults.GetDeclarationListSymbols( + Some parseResults, + 10, + " { Module1.R1. }", + { + EndColumn = 16 + LastDotPos = Some 16 + PartialIdent = "" + QualifyingIdents = [ "Module1"; "R1" ] + }, + fun _ -> List.empty + ) + |> List.concat + + hasRecordField "Field1" declarations From da4b2e7de3cca109d9122ae849e89e8c776437fb Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 12 Jul 2023 16:24:05 +0200 Subject: [PATCH 2/3] Add additional tests --- tests/service/EditorTests.fs | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 57eb82347e3..5646206628b 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -2086,3 +2086,67 @@ module Module2 = |> List.concat hasRecordField "Field1" declarations + +[] +let ``Record fields are completed in update record`` () = + let parseResults, checkResults = + getParseAndCheckResults """ +module Module + +type R1 = + { Field1: int; Field2: int } + +let r1 = { Field1 = 1; Field2 = 2 } + +let rUpdate = { r1 with } +""" + + let declarations = + checkResults.GetDeclarationListSymbols( + Some parseResults, + 9, + "let rUpdate = { r1 with }", + { + EndColumn = 24 + LastDotPos = None + PartialIdent = "" + QualifyingIdents = [] + }, + fun _ -> List.empty + ) + |> List.concat + + hasRecordField "Field1" declarations + hasRecordField "Field2" declarations + +[] +let ``Record fields are completed in update record with partial field name`` () = + let parseResults, checkResults = + getParseAndCheckResults """ +module Module + +type R1 = + { Field1: int; Field2: int } + +let r1 = { Field1 = 1; Field2 = 2 } + +let rUpdate = { r1 with } +""" + + let declarations = + checkResults.GetDeclarationListSymbols( + Some parseResults, + 9, + "let rUpdate = { r1 with Fi }", + { + EndColumn = 26 + LastDotPos = None + PartialIdent = "" + QualifyingIdents = [] + }, + fun _ -> List.empty + ) + |> List.concat + + hasRecordField "Field1" declarations + hasRecordField "Field2" declarations From bb0a725a620d3376ae297c73cf68d6a6bb5eea32 Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 12 Jul 2023 16:37:35 +0200 Subject: [PATCH 3/3] Update failing test. --- tests/service/EditorTests.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 5646206628b..8b495da8865 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -2120,6 +2120,7 @@ let rUpdate = { r1 with } hasRecordField "Field2" declarations [] +[] let ``Record fields are completed in update record with partial field name`` () = let parseResults, checkResults = getParseAndCheckResults """ @@ -2130,7 +2131,7 @@ type R1 = let r1 = { Field1 = 1; Field2 = 2 } -let rUpdate = { r1 with } +let rUpdate = { r1 with Fi } """ let declarations = @@ -2141,7 +2142,7 @@ let rUpdate = { r1 with } { EndColumn = 26 LastDotPos = None - PartialIdent = "" + PartialIdent = "Fi" QualifyingIdents = [] }, fun _ -> List.empty