Skip to content

[WIP] Support --typecheck-only for fsi run (just typecheck, no execution) #18687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
### Added
* Add opt-in warning attribute not valid for union case with fields [PR #18532](https://github.com/dotnet/fsharp/pull/18532))
* Add support for `when 'T : Enum` library-only static optimization constraint. ([PR #18546](https://github.com/dotnet/fsharp/pull/18546))
* Add `--typecheck-only` flag support for F# Interactive (FSI) scripts to type-check without execution. ([Issue #18686](https://github.com/dotnet/fsharp/issues/18686))

### Fixed

Expand Down
7 changes: 7 additions & 0 deletions src/Compiler/Driver/CompilerOptions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,13 @@ let advancedFlagsFsi tcConfigB =
None,
Some(FSComp.SR.optsClearResultsCache ())
)
CompilerOption(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option is now available for help, hence the following 4 tests are failing:

CompilerOptions\fsi\help (-?-40) -- failed
CompilerOptions\fsi\help (--help-40) -- failed
CompilerOptions\fsi\help (/?-40) -- failed
CompilerOptions\fsi\help (-? --nologo-40) -- failed

They are part of the FsharpQa suite.

This can help you, the new lines are added between clearResultsCache and exec options:

--clearResultsCache                      Clear the package manager results

                                         cache

--typecheck-only                         Perform type checking only, do not

                                         execute code

--exec                                   Exit fsi after loading the files or

                                         running the .fsx script given on the

                                         command line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated help baselines to include the new --typecheck-only option. The baseline files help40.437.1033.bsl and help40-nologo.437.1033.bsl now correctly show the new option between clearResultsCache and exec options as expected. Commit f43c85a addresses this issue.

"typecheck-only",
tagNone,
OptionUnit(fun () -> tcConfigB.typeCheckOnly <- true),
None,
Some(FSComp.SR.optsTypecheckOnly ())
)
]

let advancedFlagsFsc tcConfigB =
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,7 @@ optsVersion,"Display compiler version banner and exit"
optsResponseFile,"Read response file for more options"
optsCodepage,"Specify the codepage used to read source files"
optsClearResultsCache,"Clear the package manager results cache"
optsTypecheckOnly,"Perform type checking only, do not execute code"
optsUtf8output,"Output messages in UTF-8 encoding"
optsFullpaths,"Output messages with fully qualified paths"
optsLib,"Specify a directory for the include path which is used to resolve source files and assemblies (Short form: -I)"
Expand Down
6 changes: 6 additions & 0 deletions src/Compiler/Interactive/fsi.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2217,6 +2217,11 @@ type internal FsiDynamicCompiler
inputs
))

// typeCheckOnly either reports all errors found so far or exits with 0 - it stops processing the script
if tcConfig.typeCheckOnly then
diagnosticsLogger.AbortOnError(fsiConsoleOutput)
raise StopProcessing

let codegenResults, optEnv, fragName =
ProcessTypedImpl(
diagnosticsLogger,
Expand Down Expand Up @@ -4763,6 +4768,7 @@ type FsiEvaluationSession
let userRes =
match res with
| Choice1Of2 r -> Choice1Of2 r
| Choice2Of2 None when errorInfos.Length = 0 && tcConfigB.typeCheckOnly -> Choice1Of2 None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this change, line numbers after ilxgen for closures have shifted and therefore slightly changed ilverify baseline.

Read this repo docs for ilverify (search for it) and follow the guide for updating the baseline. It will require setting an env var and then running a script.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated ILVerify baselines to fix line number shifts caused by the code additions in fsi.fs. Updated line references from 3494→3495, 2225→2226, and 4446→4447 across all FSharp.Compiler.Service baseline files (Debug/Release, net9.0/netstandard2.0). Commit f43c85a addresses this issue.

| Choice2Of2 None ->
Choice2Of2(FsiCompilationException(FSIstrings.SR.fsiOperationCouldNotBeCompleted (), Some errorInfos) :> exn)
| Choice2Of2(Some userExn) -> Choice2Of2 userExn
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.zh-Hans.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.zh-Hant.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@
<Compile Include="Interop\ParamArray.fs" />
<Compile Include="Interop\Literals.fs" />
<Compile Include="Scripting\Interactive.fs" />
<Compile Include="Scripting\TypeCheckOnlyTests.fs" />
<Compile Include="TypeChecks\SeqTypeCheckTests.fs" />
<Compile Include="TypeChecks\CheckDeclarationsTests.fs" />
<Compile Include="TypeChecks\Graph\Utils.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module FSharp.Compiler.ComponentTests.Scripting.TypeCheckOnlyTests

open Xunit
open FSharp.Test
open FSharp.Test.Compiler

[<Fact>]
let ``typecheck-only flag works for valid script``() =
Fsx """
let x = 42
printfn "This should not execute"
exit 999 // this would have crashed if really running
"""
|> withOptions ["--typecheck-only"]
|> runFsi
|> shouldSucceed

[<Fact>]
let ``typecheck-only flag catches type errors``() =
Fsx """
let x: int = "string" // Type error
"""
|> withOptions ["--typecheck-only"]
|> runFsi
|> shouldFail
|> withStdErrContains """This expression was expected to have type"""

[<Fact>]
let ``typecheck-only flag prevents execution side effects``() =
Fsx """
printfn "MyCrazyString"
let x = 42
"""
|> withOptions ["--typecheck-only"]
|> runFsi
|> shouldSucceed
|> verifyNotInOutput "MyCrazyString"

[<Fact>]
let ``script executes without typecheck-only flag``() =
Fsx """
let x = 21+21
"""
|> withOptions ["--nologo"]
|> runFsi
|> shouldSucceed
|> verifyOutputContains [|"val x: int = 42"|]
9 changes: 9 additions & 0 deletions tests/FSharp.Test.Utilities/Compiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,15 @@ Actual:
failwith $"""Output does not match expected:{Environment.NewLine}{item}{Environment.NewLine}Actual:{Environment.NewLine}{actual}{Environment.NewLine}"""
cResult

let verifyNotInOutput (expected: string) (cResult: CompilationResult) : CompilationResult =
match getOutput cResult with
| None -> cResult
| Some actual ->
if actual.Contains(expected) then
failwith $"""Output should not contain:{Environment.NewLine}{expected}{Environment.NewLine}Actual:{Environment.NewLine}{actual}{Environment.NewLine}"""
else
cResult

type ImportScope = { Kind: ImportDefinitionKind; Name: string }

type PdbVerificationOption =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-813::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001E5][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3495-813::Invoke([S.P.CoreLib]System.Tuple`3<char[],int32,int32>)][offset 0x000001E5][found Char] Unexpected type on the stack.
[IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@106::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+dataTipOfReferences@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharpCheckerResults+dataTipOfReferences@2226::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack.
Expand Down Expand Up @@ -55,7 +55,7 @@
[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILPdbWriter+pushShadowedLocals@959::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000232][found Byte] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadUntaggedIdx([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.BinaryConstants+TableName, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+ILMetadataReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, int32&)][offset 0x0000000D][found Byte] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8<uint16,System.Tuple`2<int32,int32>,bool,bool,bool,bool,bool,System.Tuple`5<Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.AbstractIL.IL+ILPlatform>,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader>, bool)][offset 0x000007A3][found Boolean] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+rowKindSize@4445::Invoke([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+RowKind)][offset 0x00000128][found Byte] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+rowKindSize@4446::Invoke([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+RowKind)][offset 0x00000128][found Byte] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x0000000B][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x00000021][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharp.Compiler.DiagnosticsLogger::.cctor()][offset 0x000000CD][found Char] Unexpected type on the stack.
Expand Down
Loading
Loading