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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 0 additions & 4 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ jobs:
env:
CI: false
run: dotnet build ./FSharp.Compiler.Service.sln --verbosity quiet
- name: Restore the language server solution
env:
CI: false
run: dotnet build ./VSFSharpExtension.sln --verbosity quiet
- name: Restore dotnet tools
env:
CI: false
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(
"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 @@ -903,6 +903,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
4 changes: 4 additions & 0 deletions src/Compiler/Interactive/fsi.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2217,6 +2217,10 @@ type internal FsiDynamicCompiler
inputs
))

// Add this check after CheckClosedInputSet
if tcConfig.typeCheckOnly then
raise StopProcessing

let codegenResults, optEnv, fragName =
ProcessTypedImpl(
diagnosticsLogger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,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,48 @@
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"
"""
|> withOptions ["--typecheck-only"]
|> compile
|> shouldSucceed

[<Fact>]
let ``typecheck-only flag catches type errors``() =
Fsx """
let x: int = "string" // Type error
"""
|> withOptions ["--typecheck-only"]
|> compile
|> shouldFail
|> withDiagnostics [
(Error 1, Line 2, Col 14, Line 2, Col 22, "This expression was expected to have type\n 'int' \nbut here has type\n 'string'")
]

[<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 """
printfn "MyCrazyString"
let x = 42
"""
|> runFsi
|> shouldSucceed
|> verifyOutput "MyCrazyString"
19 changes: 19 additions & 0 deletions tests/FSharp.Test.Utilities/Compiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,18 @@ 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

let VerifyNotInOutput (expected: string) (cResult: CompilationResult) : CompilationResult =
verifyNotInOutput expected cResult

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

type PdbVerificationOption =
Expand Down Expand Up @@ -1906,6 +1918,13 @@ Actual:
let withStdOutContains (substring: string) (result: CompilationResult) : CompilationResult =
checkOutputInOrder "STDOUT" [substring] (fun o -> o.StdOut) result

let withStdOutNotContains (substring: string) (result: CompilationResult) : CompilationResult =
let checkOutput = fun o -> o.StdOut
let output = checkOutput result.Output
if output.Contains(substring) then
failwith $"""STDOUT should not contain:{Environment.NewLine}{substring}{Environment.NewLine}Actual:{Environment.NewLine}{output}{Environment.NewLine}"""
result

let withStdOutContainsAllInOrder (substrings: string list) (result: CompilationResult) : CompilationResult =
checkOutputInOrder "STDOUT" substrings (fun o -> o.StdOut) result

Expand Down
Loading