Skip to content

Commit 7027b34

Browse files
authored
Fix spurious 'fsi is not defined' error during literate script type-checking (#1140)
* Fix spurious 'fsi is not defined' error during literate script type-checking When literate scripts use fsi.AddPrinter or related APIs, the type-checker reported a spurious error because FSharp.Compiler.Interactive.Settings.dll was not referenced. GetProjectOptionsFromScript does not include this reference automatically in any configuration. Add a lazy resolver (fsiSettingsDll) that locates the DLL from either AppContext.BaseDirectory or the SDK's FSharp directory, and include it as an explicit -r: reference when building project options for type-checking in CodeFormatAgent. Fixes #1139 * Update RELEASE_NOTES.md for version 22.0.0-alpha.3 Add release notes for version 22.0.0-alpha.3 with fixes.
1 parent 014c199 commit 7027b34

4 files changed

Lines changed: 64 additions & 0 deletions

File tree

RELEASE_NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
## [Unreleased]
44

5+
## [22.0.0-alpha.3] - 2026-04-02
6+
57
### Fixed
8+
* Fix spurious `'fsi' is not defined` error during literate script type-checking when scripts use `fsi.AddPrinter` or related APIs. The `FSharp.Compiler.Interactive.Settings.dll` reference is now explicitly added to the type-checker options. [#1139](https://github.com/fsprojects/FSharp.Formatting/issues/1139)
69
* Fix literate script comment parser prematurely closing `(**` blocks when the markdown text contained nested `(*** ... ***)` references (e.g. in backtick-quoted command examples), causing subsequent content to be silently dropped from HTML output.
710
* Add missing `[<Test>]` attribute on `Can include-output-and-it` test so it is executed by the test runner.
811
* Add regression test confirming that types whose name matches their enclosing namespace are correctly included in generated API docs. [#944](https://github.com/fsprojects/FSharp.Formatting/issues/944)

src/FSharp.Formatting.CodeFormat/CodeFormatAgent.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,12 @@ module CodeFormatter =
421421
if Env.isNetCoreApp then
422422
yield "--targetprofile:netcore"
423423

424+
// Add FSharp.Compiler.Interactive.Settings.dll so that the
425+
// type-checker can resolve the 'fsi' object in .fsx scripts.
426+
match FSharpAssemblyHelper.fsiSettingsDll.Value with
427+
| Some path -> yield sprintf "-r:%s" path
428+
| None -> ()
429+
424430
yield! opts.OtherOptions |]
425431
|> Array.filter (fun item ->
426432
if item.StartsWith("-r:", StringComparison.Ordinal) then

src/FSharp.Formatting.Common/YaafFSharpScripting.fs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,42 @@ module internal CompilerServiceExtensions =
119119
yield! libDirs
120120
yield System.IO.Directory.GetCurrentDirectory() ]
121121

122+
/// Lazily locates an <c>FSharp.Compiler.Interactive.Settings.dll</c> so the type-checker
123+
/// can resolve the <c>fsi</c> object in <c>.fsx</c> scripts.
124+
let fsiSettingsDll =
125+
lazy
126+
(let settingsName = "FSharp.Compiler.Interactive.Settings.dll"
127+
128+
// Strategy 1: next to the running process
129+
let candidate1 = AppContext.BaseDirectory ++ settingsName
130+
131+
if File.Exists candidate1 then
132+
Some candidate1
133+
else
134+
// Strategy 2: search the SDK FSharp directory,
135+
// derived from the shared-framework location of System.Private.CoreLib.
136+
try
137+
let coreLib = typeof<obj>.Assembly.Location
138+
139+
if not (System.String.IsNullOrEmpty coreLib) then
140+
// coreLib is e.g. /usr/lib/dotnet/shared/Microsoft.NETCore.App/<ver>/System.Private.CoreLib.dll
141+
let sharedVerDir = Path.GetDirectoryName coreLib
142+
let dotnetBase = Path.GetFullPath(sharedVerDir ++ ".." ++ ".." ++ "..")
143+
let sdkDir = dotnetBase ++ "sdk"
144+
145+
if Directory.Exists sdkDir then
146+
Directory.GetDirectories sdkDir
147+
|> Array.sortDescending
148+
|> Array.tryPick (fun dir ->
149+
let c = dir ++ "FSharp" ++ settingsName
150+
if File.Exists c then Some c else None)
151+
else
152+
None
153+
else
154+
None
155+
with _ ->
156+
None)
157+
122158
/// Returns <c>Some path</c> if a file exists at <paramref name="fscorePath"/>, otherwise <c>None</c>.
123159
let tryCheckFsCore fscorePath =
124160
if File.Exists fscorePath then Some fscorePath else None

tests/FSharp.Literate.Tests/EvalTests.fs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,3 +712,22 @@ Wrapped "TRANSFORMED"
712712

713713
let html1 = Literate.ToHtml(doc1)
714714
html1 |> shouldContainText "<em>TRANSFORMED</em>"
715+
716+
[<Test>]
717+
let ``fsi.AddPrinter does not produce spurious 'fsi is not defined' diagnostic`` () =
718+
let content =
719+
"""
720+
type Foo = { X: int }
721+
fsi.AddPrinter(fun (f: Foo) -> sprintf "FOO(%d)" f.X)
722+
let v = { X = 42 }
723+
v
724+
(*** include-it ***)
725+
"""
726+
727+
let fsie = getFsiEvaluator ()
728+
729+
let doc1 = Literate.ParseScriptString(content, "." </> "A.fsx", fsiEvaluator = fsie)
730+
731+
doc1.Diagnostics.Length |> shouldEqual 0
732+
let html1 = Literate.ToHtml(doc1)
733+
html1 |> shouldContainText "FOO(42)"

0 commit comments

Comments
 (0)