From 46dcf973d072b0862a7bed798e3f694c44099862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Jank=C3=B3?= Date: Fri, 12 Dec 2025 17:17:55 +0100 Subject: [PATCH 1/2] add optional keepAssemblyContents to ParseAndCheckInteraction --- src/Compiler/Interactive/fsi.fs | 27 +++++++++++++++---- src/Compiler/Interactive/fsi.fsi | 3 ++- src/Compiler/Service/FSharpCheckerResults.fs | 20 +++++++++----- src/Compiler/Service/FSharpCheckerResults.fsi | 9 +++++-- ...iler.Service.SurfaceArea.netstandard20.bsl | 2 +- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index ca96324426a..7f40350dfe4 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -3081,6 +3081,9 @@ type internal FsiDynamicCompiler member _.ValueBound = valueBoundEvent.Publish + member _.PeekNextFragmentPath() = + FsiDynamicModulePrefix + $"%04d{fragmentId + 1}" + //---------------------------------------------------------------------------- // ctrl-c handling //---------------------------------------------------------------------------- @@ -4450,13 +4453,22 @@ type FsiInteractionProcessor let names = names |> List.filter (fun name -> name.StartsWithOrdinal(stem)) names - member _.ParseAndCheckInteraction(legacyReferenceResolver, istate, text: string) = + member _.ParseAndCheckInteraction(legacyReferenceResolver, istate, text: string, ?keepAssemblyContents: bool) = let tcConfig = TcConfig.Create(tcConfigB, validate = false) + let nextFragmentPath = fsiDynamicCompiler.PeekNextFragmentPath() + let fsiInteractiveChecker = - FsiInteractiveChecker(legacyReferenceResolver, tcConfig, istate.tcGlobals, istate.tcImports, istate.tcState) + FsiInteractiveChecker( + legacyReferenceResolver, + tcConfig, + istate.tcGlobals, + istate.tcImports, + istate.tcState, + ?keepAssemblyContents = keepAssemblyContents + ) - fsiInteractiveChecker.ParseAndCheckInteraction(SourceText.ofString text) + fsiInteractiveChecker.ParseAndCheckInteraction(SourceText.ofString text, asmName = nextFragmentPath) //---------------------------------------------------------------------------- // Server mode: @@ -4808,8 +4820,13 @@ type FsiEvaluationSession fsiInteractionProcessor.CompletionsForPartialLID(fsiInteractionProcessor.CurrentState, longIdent) |> Seq.ofList - member _.ParseAndCheckInteraction(code) = - fsiInteractionProcessor.ParseAndCheckInteraction(legacyReferenceResolver, fsiInteractionProcessor.CurrentState, code) + member _.ParseAndCheckInteraction(code, ?keepAssemblyContents) = + fsiInteractionProcessor.ParseAndCheckInteraction( + legacyReferenceResolver, + fsiInteractionProcessor.CurrentState, + code, + ?keepAssemblyContents = keepAssemblyContents + ) |> Cancellable.runWithoutCancellation member _.InteractiveChecker = checker diff --git a/src/Compiler/Interactive/fsi.fsi b/src/Compiler/Interactive/fsi.fsi index eebef0e63df..ab6a624daf1 100644 --- a/src/Compiler/Interactive/fsi.fsi +++ b/src/Compiler/Interactive/fsi.fsi @@ -282,7 +282,8 @@ type FsiEvaluationSession = /// /// Operations may be run concurrently with other requests to the InteractiveChecker. member ParseAndCheckInteraction: - code: string -> FSharpParseFileResults * FSharpCheckFileResults * FSharpCheckProjectResults + code: string * ?keepAssemblyContents: bool -> + FSharpParseFileResults * FSharpCheckFileResults * FSharpCheckProjectResults /// The single, global interactive checker to use in conjunction with other operations /// on the FsiEvaluationSession. diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 584591c059c..38dc8903ed2 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -3990,14 +3990,16 @@ type FSharpCheckProjectResults override _.ToString() = "FSharpCheckProjectResults(" + projectFileName + ")" -type FsiInteractiveChecker(legacyReferenceResolver, tcConfig: TcConfig, tcGlobals: TcGlobals, tcImports: TcImports, tcState) = +type FsiInteractiveChecker + (legacyReferenceResolver, tcConfig: TcConfig, tcGlobals: TcGlobals, tcImports: TcImports, tcState, ?keepAssemblyContents: bool) = - let keepAssemblyContents = false + let keepAssemblyContents = defaultArg keepAssemblyContents false - member _.ParseAndCheckInteraction(sourceText: ISourceText, ?userOpName: string) = + member _.ParseAndCheckInteraction(sourceText: ISourceText, ?userOpName: string, ?asmName: string) = cancellable { let userOpName = defaultArg userOpName "Unknown" - let fileName = Path.Combine(tcConfig.implicitIncludeDir, "stdin.fsx") + let asmName = defaultArg asmName "stdin" + let fileName = Path.Combine(tcConfig.implicitIncludeDir, asmName + ".fsx") let suggestNamesForErrors = true // Will always be true, this is just for readability // Note: projectSourceFiles is only used to compute isLastCompiland, and is ignored if Build.IsScript(mainInputFileName) is true (which it is in this case). let parsingOptions = @@ -4087,6 +4089,12 @@ type FsiInteractiveChecker(legacyReferenceResolver, tcConfig: TcConfig, tcGlobal let typeCheckResults = FSharpCheckFileResults(fileName, errors, Some tcFileInfo, dependencyFiles, None, false) + let checkedImplFiles = + if keepAssemblyContents then + tcFileInfo.ImplementationFile |> Option.map List.singleton + else + None + let details = (tcGlobals, tcImports, @@ -4095,9 +4103,9 @@ type FsiInteractiveChecker(legacyReferenceResolver, tcConfig: TcConfig, tcGlobal Choice2Of2(tcFileInfo.ScopeSymbolUses |> Seq.singleton |> async.Return), None, (fun () -> None), - mkSimpleAssemblyRef "stdin", + mkSimpleAssemblyRef asmName, tcState.TcEnvFromImpls.AccessRights, - None, + checkedImplFiles, dependencyFiles, Some projectOptions) diff --git a/src/Compiler/Service/FSharpCheckerResults.fsi b/src/Compiler/Service/FSharpCheckerResults.fsi index 28deec6804a..b0250d9fa1d 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fsi +++ b/src/Compiler/Service/FSharpCheckerResults.fsi @@ -620,11 +620,16 @@ module internal ParseAndCheckFile = // Used internally to provide intellisense over F# Interactive. type internal FsiInteractiveChecker = internal new: - LegacyReferenceResolver * tcConfig: TcConfig * tcGlobals: TcGlobals * tcImports: TcImports * tcState: TcState -> + LegacyReferenceResolver * + tcConfig: TcConfig * + tcGlobals: TcGlobals * + tcImports: TcImports * + tcState: TcState * + ?keepAssemblyContents: bool -> FsiInteractiveChecker member internal ParseAndCheckInteraction: - sourceText: ISourceText * ?userOpName: string -> + sourceText: ISourceText * ?userOpName: string * ?asmName: string -> Cancellable module internal FSharpCheckerResultsSettings = diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl index bd46be604f5..5a50de983a2 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl @@ -4984,7 +4984,7 @@ FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: System.Tuple`2[Microsoft FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: System.Tuple`2[Microsoft.FSharp.Core.FSharpChoice`2[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Interactive.Shell+FsiValue],System.Exception],FSharp.Compiler.Diagnostics.FSharpDiagnostic[]] EvalInteractionNonThrowing(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: System.Tuple`2[Microsoft.FSharp.Core.FSharpChoice`2[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Interactive.Shell+FsiValue],System.Exception],FSharp.Compiler.Diagnostics.FSharpDiagnostic[]] EvalInteractionNonThrowing(System.String, System.String, Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: System.Tuple`2[Microsoft.FSharp.Core.FSharpChoice`2[Microsoft.FSharp.Core.Unit,System.Exception],FSharp.Compiler.Diagnostics.FSharpDiagnostic[]] EvalScriptNonThrowing(System.String) -FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: System.Tuple`3[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckInteraction(System.String) +FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: System.Tuple`3[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults,FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults] ParseAndCheckInteraction(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: Void AddBoundValue(System.String, System.Object) FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: Void EvalInteraction(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) FSharp.Compiler.Interactive.Shell+FsiEvaluationSession: Void EvalInteraction(System.String, System.String, Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) From 13755a3685115bc87a878bf1226706cb3da75824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Jank=C3=B3?= Date: Mon, 15 Dec 2025 14:40:16 +0100 Subject: [PATCH 2/2] expand XML docs --- src/Compiler/Interactive/fsi.fsi | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Interactive/fsi.fsi b/src/Compiler/Interactive/fsi.fsi index ab6a624daf1..14bda032a76 100644 --- a/src/Compiler/Interactive/fsi.fsi +++ b/src/Compiler/Interactive/fsi.fsi @@ -276,11 +276,17 @@ type FsiEvaluationSession = /// This event is triggered after parsing and checking, either via input from 'stdin', or via a call to EvalInteraction. member PartialAssemblySignatureUpdated: IEvent - /// Typecheck the given script fragment in the type checking context implied by the current state - /// of F# Interactive. The results can be used to access intellisense, perform resolutions, - /// check brace matching and other information. + /// + /// Typecheck the given script fragment in the type checking context implied by the current state + /// of F# Interactive. The results can be used to access intellisense, perform resolutions, + /// check brace matching and other information. Accessing assembly contents need 'keepAssemblyContents' + /// set to 'true'. + /// + /// Operations may be run concurrently with other requests to the InteractiveChecker. + /// /// - /// Operations may be run concurrently with other requests to the InteractiveChecker. + /// Source code of the interaction to check. + /// Keep the checked contents of the interaction. member ParseAndCheckInteraction: code: string * ?keepAssemblyContents: bool -> FSharpParseFileResults * FSharpCheckFileResults * FSharpCheckProjectResults