From ecd36549df87423f0329f1cb08dd87d2a61f33a9 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 28 Apr 2025 17:13:50 +0200 Subject: [PATCH 1/5] Completion: use first environment with range --- src/Compiler/Service/FSharpCheckerResults.fs | 2 +- .../CompletionTests.fs | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 57d322c81a8..e8991566125 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -384,7 +384,7 @@ type internal TypeCheckInfo if rangeContainsPos mPossible cursorPos then match bestSoFar with | Some(bestm, _, _) -> - if rangeContainsRange bestm mPossible then + if rangeContainsRange bestm mPossible && not (equals bestm mPossible) then bestSoFar <- Some(mPossible, env, ad) | None -> bestSoFar <- Some(mPossible, env, ad)) diff --git a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs index 38c6812728d..c5b19d130ed 100644 --- a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs @@ -134,3 +134,25 @@ let f (s: string) = () """ assertHasItemWithNames ["Length"] info + + +[] +let ``Import - Ns 01`` () = + let info = + getCompletionInfo "let _: R " (14, 12) """ +namespace Ns + +type Rec1 = { F: int } + + +namespace Ns + +type Rec2 = { F: int } + +module M = + + type Rec3 = { F: int } + + let _: R = () +""" + assertHasItemWithNames ["Rec1"; "Rec2"; "Rec3"] info From 523af79363711987e25d60e4284209e685cfc26d Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 28 Apr 2025 17:59:22 +0200 Subject: [PATCH 2/5] Fix code completion for types previously defined in own namespace --- src/Compiler/Checking/CheckDeclarations.fs | 14 ++++--- src/Compiler/Checking/CheckDeclarations.fsi | 2 +- src/Compiler/Driver/ParseAndCheckInputs.fs | 6 +-- src/Compiler/Service/FSharpCheckerResults.fs | 2 +- .../CompletionTests.fs | 42 +++++++++++++++++++ 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 78acf62bb98..4c357199587 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -341,7 +341,7 @@ let AddNonLocalCcu g amap scopem env assemblyName (ccu: CcuThunk, internalsVisib env /// Adjust the TcEnv to account for a fully processed "namespace" declaration in this file -let AddLocalRootModuleOrNamespace tcSink g amap scopem env (moduleTy: ModuleOrNamespaceType) = +let AddLocalRootModuleOrNamespace captureEnv tcSink g amap scopem env (moduleTy: ModuleOrNamespaceType) = // Compute the top-rooted module or namespace references let modrefs = moduleTy.ModuleAndNamespaceDefinitions |> List.map mkLocalModuleRef // Compute the top-rooted type definitions @@ -350,7 +350,9 @@ let AddLocalRootModuleOrNamespace tcSink g amap scopem env (moduleTy: ModuleOrNa let env = { env with eNameResEnv = if isNil tcrefs then env.eNameResEnv else AddTyconRefsToNameEnv BulkAdd.No false g amap env.eAccessRights scopem true env.eNameResEnv tcrefs eUngeneralizableItems = addFreeItemOfModuleTy moduleTy env.eUngeneralizableItems } - CallEnvSink tcSink (scopem, env.NameEnv, env.eAccessRights) + + if captureEnv then + CallEnvSink tcSink (scopem, env.NameEnv, env.eAccessRights) env /// Inside "namespace X.Y.Z" there is an implicit open of "X.Y.Z" @@ -4978,7 +4980,7 @@ let rec TcSignatureElementNonMutRec (cenv: cenv) parent typeNames endm (env: TcE CallNameResolutionSink cenv.tcSink (moduleEntity.Range, env.NameEnv, item, emptyTyparInst, ItemOccurrence.Binding, env.AccessRights)) // For 'namespace rec' and 'module rec' we add the thing being defined - let envNS = if isRec then AddLocalRootModuleOrNamespace cenv.tcSink g cenv.amap m envNS modTyRoot else envNS + let envNS = if isRec then AddLocalRootModuleOrNamespace true cenv.tcSink g cenv.amap m envNS modTyRoot else envNS let nsInfo = Some (modulNSOpt, envNS.eModuleOrNamespaceTypeAccumulator) let mutRecNSInfo = if isRec then nsInfo else None @@ -4990,7 +4992,7 @@ let rec TcSignatureElementNonMutRec (cenv: cenv) parent typeNames endm (env: TcE if isNil enclosingNamespacePath then envAtEnd else - let env = AddLocalRootModuleOrNamespace cenv.tcSink g cenv.amap m env modTyRoot + let env = AddLocalRootModuleOrNamespace false cenv.tcSink g cenv.amap m env modTyRoot // If the namespace is an interactive fragment e.g. FSI_0002, then open FSI_0002 in the subsequent environment. let env, _openDecls = @@ -5440,7 +5442,7 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem CallNameResolutionSink cenv.tcSink (moduleEntity.Range, env.NameEnv, item, emptyTyparInst, ItemOccurrence.Binding, env.AccessRights)) // For 'namespace rec' and 'module rec' we add the thing being defined - let envNS = if isRec then AddLocalRootModuleOrNamespace cenv.tcSink g cenv.amap m envNS modTyRoot else envNS + let envNS = if isRec then AddLocalRootModuleOrNamespace true cenv.tcSink g cenv.amap m envNS modTyRoot else envNS let nsInfo = Some (modulNSOpt, envNS.eModuleOrNamespaceTypeAccumulator) let mutRecNSInfo = if isRec then nsInfo else None @@ -5453,7 +5455,7 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem if isNil enclosingNamespacePath then envAtEnd, [] else - let env = AddLocalRootModuleOrNamespace cenv.tcSink g cenv.amap m env modTyRoot + let env = AddLocalRootModuleOrNamespace true cenv.tcSink g cenv.amap m env modTyRoot // If the namespace is an interactive fragment e.g. FSI_0002, then open FSI_0002 in the subsequent environment let env, openDecls = diff --git a/src/Compiler/Checking/CheckDeclarations.fsi b/src/Compiler/Checking/CheckDeclarations.fsi index fb4679f2438..ad1012b759b 100644 --- a/src/Compiler/Checking/CheckDeclarations.fsi +++ b/src/Compiler/Checking/CheckDeclarations.fsi @@ -14,7 +14,7 @@ open FSharp.Compiler.Text open FSharp.Compiler.TypedTree val AddLocalRootModuleOrNamespace: - TcResultsSink -> TcGlobals -> ImportMap -> range -> TcEnv -> ModuleOrNamespaceType -> TcEnv + bool -> TcResultsSink -> TcGlobals -> ImportMap -> range -> TcEnv -> ModuleOrNamespaceType -> TcEnv val CreateInitialTcEnv: TcGlobals * ImportMap * range * assemblyName: string * (CcuThunk * string list * string list) list -> diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index 6b7171b5697..17f94cf0a21 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1198,14 +1198,14 @@ let AddCheckResultsToTcState // Add the implementation as to the implementation env let tcImplEnv = - AddLocalRootModuleOrNamespace TcResultsSink.NoSink tcGlobals amap m tcImplEnv implFileSigType + AddLocalRootModuleOrNamespace true TcResultsSink.NoSink tcGlobals amap m tcImplEnv implFileSigType // Add the implementation as to the signature env (unless it had an explicit signature) let tcSigEnv = if hadSig then tcState.tcsTcSigEnv else - AddLocalRootModuleOrNamespace TcResultsSink.NoSink tcGlobals amap m tcState.tcsTcSigEnv implFileSigType + AddLocalRootModuleOrNamespace true TcResultsSink.NoSink tcGlobals amap m tcState.tcsTcSigEnv implFileSigType // Open the prefixPath for fsi.exe (tcImplEnv) let tcImplEnv, openDecls = @@ -1563,7 +1563,7 @@ let CheckOneInputWithCallback let rootSigs = Zmap.add qualNameOfFile sigFileType tcState.tcsRootSigs let tcSigEnv = - AddLocalRootModuleOrNamespace TcResultsSink.NoSink tcGlobals amap m tcState.tcsTcSigEnv sigFileType + AddLocalRootModuleOrNamespace true TcResultsSink.NoSink tcGlobals amap m tcState.tcsTcSigEnv sigFileType // Add the signature to the signature env (unless it had an explicit signature) let ccuSigForFile = CombineCcuContentFragments [ sigFileType; tcState.tcsCcuSig ] diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index e8991566125..57d322c81a8 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -384,7 +384,7 @@ type internal TypeCheckInfo if rangeContainsPos mPossible cursorPos then match bestSoFar with | Some(bestm, _, _) -> - if rangeContainsRange bestm mPossible && not (equals bestm mPossible) then + if rangeContainsRange bestm mPossible then bestSoFar <- Some(mPossible, env, ad) | None -> bestSoFar <- Some(mPossible, env, ad)) diff --git a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs index c5b19d130ed..c4a924d25d9 100644 --- a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs @@ -156,3 +156,45 @@ module M = let _: R = () """ assertHasItemWithNames ["Rec1"; "Rec2"; "Rec3"] info + +[] +let ``Import - Ns 02 - Rec`` () = + let info = + getCompletionInfo "let _: R " (14, 12) """ +namespace Ns + +type Rec1 = { F: int } + + +namespace rec Ns + +type Rec2 = { F: int } + +module M = + + type Rec3 = { F: int } + + let _: R = () +""" + assertHasItemWithNames ["Rec1"; "Rec2"; "Rec3"] info + +[] +let ``Import - Ns 03 - Rec`` () = + let info = + getCompletionInfo "let _: R " (14, 12) """ +namespace Ns + +type Rec1 = { F: int } + + +namespace rec Ns + +type Rec2 = { F: int } + +module rec M = + + type Rec3 = { F: int } + + let _: R = () +""" + assertHasItemWithNames ["Rec1"; "Rec2"; "Rec3"] info \ No newline at end of file From 9037bbdd5cae1e109629fb4183943ad0aea9ee70 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 28 Apr 2025 20:12:32 +0200 Subject: [PATCH 3/5] Never capture checked module environment --- src/Compiler/Checking/CheckDeclarations.fs | 13 +++++-------- src/Compiler/Checking/CheckDeclarations.fsi | 2 +- src/Compiler/Driver/ParseAndCheckInputs.fs | 6 +++--- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 4c357199587..92368118f3a 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -341,7 +341,7 @@ let AddNonLocalCcu g amap scopem env assemblyName (ccu: CcuThunk, internalsVisib env /// Adjust the TcEnv to account for a fully processed "namespace" declaration in this file -let AddLocalRootModuleOrNamespace captureEnv tcSink g amap scopem env (moduleTy: ModuleOrNamespaceType) = +let AddLocalRootModuleOrNamespace g amap scopem env (moduleTy: ModuleOrNamespaceType) = // Compute the top-rooted module or namespace references let modrefs = moduleTy.ModuleAndNamespaceDefinitions |> List.map mkLocalModuleRef // Compute the top-rooted type definitions @@ -350,9 +350,6 @@ let AddLocalRootModuleOrNamespace captureEnv tcSink g amap scopem env (moduleTy: let env = { env with eNameResEnv = if isNil tcrefs then env.eNameResEnv else AddTyconRefsToNameEnv BulkAdd.No false g amap env.eAccessRights scopem true env.eNameResEnv tcrefs eUngeneralizableItems = addFreeItemOfModuleTy moduleTy env.eUngeneralizableItems } - - if captureEnv then - CallEnvSink tcSink (scopem, env.NameEnv, env.eAccessRights) env /// Inside "namespace X.Y.Z" there is an implicit open of "X.Y.Z" @@ -4980,7 +4977,7 @@ let rec TcSignatureElementNonMutRec (cenv: cenv) parent typeNames endm (env: TcE CallNameResolutionSink cenv.tcSink (moduleEntity.Range, env.NameEnv, item, emptyTyparInst, ItemOccurrence.Binding, env.AccessRights)) // For 'namespace rec' and 'module rec' we add the thing being defined - let envNS = if isRec then AddLocalRootModuleOrNamespace true cenv.tcSink g cenv.amap m envNS modTyRoot else envNS + let envNS = if isRec then AddLocalRootModuleOrNamespace g cenv.amap m envNS modTyRoot else envNS let nsInfo = Some (modulNSOpt, envNS.eModuleOrNamespaceTypeAccumulator) let mutRecNSInfo = if isRec then nsInfo else None @@ -4992,7 +4989,7 @@ let rec TcSignatureElementNonMutRec (cenv: cenv) parent typeNames endm (env: TcE if isNil enclosingNamespacePath then envAtEnd else - let env = AddLocalRootModuleOrNamespace false cenv.tcSink g cenv.amap m env modTyRoot + let env = AddLocalRootModuleOrNamespace g cenv.amap m env modTyRoot // If the namespace is an interactive fragment e.g. FSI_0002, then open FSI_0002 in the subsequent environment. let env, _openDecls = @@ -5442,7 +5439,7 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem CallNameResolutionSink cenv.tcSink (moduleEntity.Range, env.NameEnv, item, emptyTyparInst, ItemOccurrence.Binding, env.AccessRights)) // For 'namespace rec' and 'module rec' we add the thing being defined - let envNS = if isRec then AddLocalRootModuleOrNamespace true cenv.tcSink g cenv.amap m envNS modTyRoot else envNS + let envNS = if isRec then AddLocalRootModuleOrNamespace g cenv.amap m envNS modTyRoot else envNS let nsInfo = Some (modulNSOpt, envNS.eModuleOrNamespaceTypeAccumulator) let mutRecNSInfo = if isRec then nsInfo else None @@ -5455,7 +5452,7 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem if isNil enclosingNamespacePath then envAtEnd, [] else - let env = AddLocalRootModuleOrNamespace true cenv.tcSink g cenv.amap m env modTyRoot + let env = AddLocalRootModuleOrNamespace g cenv.amap m env modTyRoot // If the namespace is an interactive fragment e.g. FSI_0002, then open FSI_0002 in the subsequent environment let env, openDecls = diff --git a/src/Compiler/Checking/CheckDeclarations.fsi b/src/Compiler/Checking/CheckDeclarations.fsi index ad1012b759b..b25d454ac42 100644 --- a/src/Compiler/Checking/CheckDeclarations.fsi +++ b/src/Compiler/Checking/CheckDeclarations.fsi @@ -14,7 +14,7 @@ open FSharp.Compiler.Text open FSharp.Compiler.TypedTree val AddLocalRootModuleOrNamespace: - bool -> TcResultsSink -> TcGlobals -> ImportMap -> range -> TcEnv -> ModuleOrNamespaceType -> TcEnv + TcGlobals -> ImportMap -> range -> TcEnv -> ModuleOrNamespaceType -> TcEnv val CreateInitialTcEnv: TcGlobals * ImportMap * range * assemblyName: string * (CcuThunk * string list * string list) list -> diff --git a/src/Compiler/Driver/ParseAndCheckInputs.fs b/src/Compiler/Driver/ParseAndCheckInputs.fs index 17f94cf0a21..3d9884b2cef 100644 --- a/src/Compiler/Driver/ParseAndCheckInputs.fs +++ b/src/Compiler/Driver/ParseAndCheckInputs.fs @@ -1198,14 +1198,14 @@ let AddCheckResultsToTcState // Add the implementation as to the implementation env let tcImplEnv = - AddLocalRootModuleOrNamespace true TcResultsSink.NoSink tcGlobals amap m tcImplEnv implFileSigType + AddLocalRootModuleOrNamespace tcGlobals amap m tcImplEnv implFileSigType // Add the implementation as to the signature env (unless it had an explicit signature) let tcSigEnv = if hadSig then tcState.tcsTcSigEnv else - AddLocalRootModuleOrNamespace true TcResultsSink.NoSink tcGlobals amap m tcState.tcsTcSigEnv implFileSigType + AddLocalRootModuleOrNamespace tcGlobals amap m tcState.tcsTcSigEnv implFileSigType // Open the prefixPath for fsi.exe (tcImplEnv) let tcImplEnv, openDecls = @@ -1563,7 +1563,7 @@ let CheckOneInputWithCallback let rootSigs = Zmap.add qualNameOfFile sigFileType tcState.tcsRootSigs let tcSigEnv = - AddLocalRootModuleOrNamespace true TcResultsSink.NoSink tcGlobals amap m tcState.tcsTcSigEnv sigFileType + AddLocalRootModuleOrNamespace tcGlobals amap m tcState.tcsTcSigEnv sigFileType // Add the signature to the signature env (unless it had an explicit signature) let ccuSigForFile = CombineCcuContentFragments [ sigFileType; tcState.tcsCcuSig ] From 831ebf5003a65d94e2ae3f96b292d20f8231a9a6 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 28 Apr 2025 20:18:34 +0200 Subject: [PATCH 4/5] Fantomas --- src/Compiler/Checking/CheckDeclarations.fsi | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fsi b/src/Compiler/Checking/CheckDeclarations.fsi index b25d454ac42..9b06fcc828d 100644 --- a/src/Compiler/Checking/CheckDeclarations.fsi +++ b/src/Compiler/Checking/CheckDeclarations.fsi @@ -13,8 +13,7 @@ open FSharp.Compiler.TcGlobals open FSharp.Compiler.Text open FSharp.Compiler.TypedTree -val AddLocalRootModuleOrNamespace: - TcGlobals -> ImportMap -> range -> TcEnv -> ModuleOrNamespaceType -> TcEnv +val AddLocalRootModuleOrNamespace: TcGlobals -> ImportMap -> range -> TcEnv -> ModuleOrNamespaceType -> TcEnv val CreateInitialTcEnv: TcGlobals * ImportMap * range * assemblyName: string * (CcuThunk * string list * string list) list -> From 65025b54f9fa392cfa939106c8f6ebc694ddd6b6 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 28 Apr 2025 22:00:15 +0200 Subject: [PATCH 5/5] Release notes --- docs/release-notes/.FSharp.Compiler.Service/9.0.300.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index 16f5721e22a..aa4e6eea89a 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -26,6 +26,7 @@ * Fix confusing type inference error in task expression ([Issue #13789](https://github.com/dotnet/fsharp/issues/13789), [PR #18450](https://github.com/dotnet/fsharp/pull/18450)) * Fix missing `null` highlighting in tooltips ([PR #18457](https://github.com/dotnet/fsharp/pull/18457)) * Make `[]` combination work([PR #18444](https://github.com/dotnet/fsharp/pull/18444/)) +* Fix code completion considers types from own namespace non-imported ([PR #18518](https://github.com/dotnet/fsharp/issues/18518)) ### Added * Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241))