From a56015cec0e9d7ad5879218ff24206d0a38df6c9 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 15 May 2026 14:53:57 +0200 Subject: [PATCH 01/11] Add failing tests for #19576 (TDD red) Tests demonstrate that --nowarn:N does not suppress warnings emitted during command-line option parsing (e.g. FS0075 for internal/test-only options like --extraoptimizationloops, --typedtree). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Fsc/warn/nowarn_for_cmdline_warnings.fs | 48 +++++++++++++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 1 + 2 files changed, 49 insertions(+) create mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs new file mode 100644 index 00000000000..a7e55895779 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace CompilerOptions.Fsc + +open Xunit +open FSharp.Test.Compiler + +/// Regression tests for https://github.com/dotnet/fsharp/issues/19576 +/// +/// Warnings emitted during command-line option parsing (e.g. FS0075 for internal/test-only +/// options, FS3211 for duplicate source files, the test-switch unknown-arg warning) must +/// honor `--nowarn:` just like any other compiler warning. +module ``Nowarn for command-line option warnings`` = + + // FS0075: "The command-line option '%s' is for test purposes only" + // Emitted by reportDeprecatedOption in CompilerOptions.fs when an InternalCommandLineOption + // is used (e.g. --extraoptimizationloops). + [] + let ``--nowarn 75 suppresses FS0075 for --extraoptimizationloops`` () = + FSharp "module Module" + |> withNoWarn 75 + |> withOptions ["--extraoptimizationloops:1"] + |> compile + |> shouldSucceed + + [] + let ``--nowarn 75 suppresses FS0075 for --typedtree`` () = + FSharp "module Module" + |> withNoWarn 75 + |> withOptions ["--typedtree"] + |> compile + |> shouldSucceed + + // FS0075 also covers the unknown --test: argument warning emitted from CompilerOptions.fs + // (line ~1465: warning (Error(FSComp.SR.optsUnknownArgumentToTheTestSwitch str, ...))) + // That message currently uses error number 75 as well because it shares the + // InternalCommandLineOption phrase family (`buildArgInvalidInt` etc. behavior). + // If, on your branch, the unknown --test: argument turns out to use a different number, + // adjust the nowarn argument accordingly. The point is: --nowarn should silence it. + [] + let ``--nowarn for the --test unknown-arg warning suppresses it`` () = + // Use --test:NotARealTestArg to trigger optsUnknownArgumentToTheTestSwitch. + // Look up its FS error number; in current main it surfaces as FS1052 + // (buildUnrecognizedOption family). Cover both common numbers via a wider nowarn list. + FSharp "module Module" + |> withOptions ["--nowarn:75"; "--nowarn:1052"; "--test:NotARealTestArg"] + |> compile + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 50937f9945b..dfc14e9ef4d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -419,6 +419,7 @@ + From eed543a36b48699d7e6b3ae77323a31f977f7cc7 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 15 May 2026 15:40:21 +0200 Subject: [PATCH 02/11] Honor --nowarn for warnings captured during command-line option parsing (#19576) Wrap the logger used to replay delayed command-line diagnostics with GetDiagnosticsLoggerFilteringByScopedNowarn so that --nowarn / --warnaserror / warn-level switches set on the command line are honored for diagnostics captured during option parsing (e.g. FS0075 from internal/test-only flags such as --extraoptimizationloops or --typedtree). The fix is applied both on the success path (fsc.fs main1, line ~586) and on the error/exit paths via the IDiagnosticsLoggerProvider extension method, making it universal across fsc.exe and FSharpChecker.Compile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Driver/fsc.fs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index 3b6a3bfef18..968d9075368 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -120,9 +120,14 @@ type IDiagnosticsLoggerProvider = type CapturingDiagnosticsLogger with /// Commit the delayed diagnostics via a fresh temporary logger of the right kind. - member x.CommitDelayedDiagnostics(diagnosticsLoggerProvider: IDiagnosticsLoggerProvider, tcConfigB, exiter) = + /// Wraps the target logger with a filter so that --nowarn / --warnaserror / warn level + /// set on the command line are honored for diagnostics that were captured *during* + /// command-line option parsing (e.g. FS0075 from internal/test-only flags). + member x.CommitDelayedDiagnostics(diagnosticsLoggerProvider: IDiagnosticsLoggerProvider, tcConfigB: TcConfigBuilder, exiter) = let diagnosticsLogger = diagnosticsLoggerProvider.CreateLogger(tcConfigB, exiter) - x.CommitDelayedDiagnostics diagnosticsLogger + let filteredLogger = + GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger) + x.CommitDelayedDiagnostics filteredLogger /// The default DiagnosticsLogger implementation, reporting messages to the Console up to the maxerrors maximum type ConsoleLoggerProvider() = @@ -577,8 +582,12 @@ let main1 // Install the global error logger and never remove it. This logger does have all command-line flags considered. SetThreadDiagnosticsLoggerNoUnwind diagnosticsLogger - // Forward all errors from flags - delayForFlagsLogger.CommitDelayedDiagnostics diagnosticsLogger + // Forward all errors from flags, filtering by command-line --nowarn/--warnaserror so that + // diagnostics captured during option parsing honor those switches (see issue #19576). + let delayedFlagsCommitLogger = + GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger) + + delayForFlagsLogger.CommitDelayedDiagnostics delayedFlagsCommitLogger if not tcConfigB.continueAfterParseFailure then AbortOnError(diagnosticsLogger, exiter) From adfa645b3acdeec19eded1bdd4c88ad557fb32c5 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 15 May 2026 16:24:59 +0200 Subject: [PATCH 03/11] Remove speculative third test for --test unknown-arg The third test added in TDD red phase was speculative (warning numbers didn't match) and outside the scope of sprint 2's DoD. Sprint 2 only required the first two tests (FS0075 for --extraoptimizationloops and --typedtree) to pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Fsc/warn/nowarn_for_cmdline_warnings.fs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs index a7e55895779..922d0925f58 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs @@ -31,18 +31,3 @@ module ``Nowarn for command-line option warnings`` = |> compile |> shouldSucceed - // FS0075 also covers the unknown --test: argument warning emitted from CompilerOptions.fs - // (line ~1465: warning (Error(FSComp.SR.optsUnknownArgumentToTheTestSwitch str, ...))) - // That message currently uses error number 75 as well because it shares the - // InternalCommandLineOption phrase family (`buildArgInvalidInt` etc. behavior). - // If, on your branch, the unknown --test: argument turns out to use a different number, - // adjust the nowarn argument accordingly. The point is: --nowarn should silence it. - [] - let ``--nowarn for the --test unknown-arg warning suppresses it`` () = - // Use --test:NotARealTestArg to trigger optsUnknownArgumentToTheTestSwitch. - // Look up its FS error number; in current main it surfaces as FS1052 - // (buildUnrecognizedOption family). Cover both common numbers via a wider nowarn list. - FSharp "module Module" - |> withOptions ["--nowarn:75"; "--nowarn:1052"; "--test:NotARealTestArg"] - |> compile - |> shouldSucceed From 46c69eb4d07d931f77a1234b3ef0581ff09b1e41 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 15 May 2026 16:53:03 +0200 Subject: [PATCH 04/11] Centralize warning emission in CompilerOptions.fs (#19576 follow-up) Introduce a local 'warningCmdLine' helper that consults tcConfigB.diagnosticsOptions via PhasedDiagnostic.AdjustSeverity before forwarding to warning/errorR/informationalWarning. This gives option parsing a second, local line of defense so future 'warning' callsites in CompilerOptions.fs cannot silently bypass --nowarn/--warnaserror even if the commit-time delayed-diagnostics wrapper is missing. - Thread TcConfigBuilder through ParseCompilerOptions (all 6 callers). - Thread TcConfigBuilder through CheckAndReportSourceFileDuplicates. - Route the three cowboy 'warning' callsites in CompilerOptions.fs (reportDeprecatedOption, --test unknown-arg, duplicate source file) through warningCmdLine. - Add a regression test for FS1063 (--test unknown sub-flag). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Driver/CompilerOptions.fs | 31 ++++++++++++++----- src/Compiler/Driver/CompilerOptions.fsi | 4 +-- src/Compiler/Driver/fsc.fs | 6 ++-- src/Compiler/Interactive/fsi.fs | 2 +- src/Compiler/Service/BackgroundCompiler.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fs | 2 +- src/Compiler/Service/TransparentCompiler.fs | 2 +- .../Fsc/warn/nowarn_for_cmdline_warnings.fs | 10 ++++++ 8 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/Compiler/Driver/CompilerOptions.fs b/src/Compiler/Driver/CompilerOptions.fs index 98cec17265c..118b5fc446d 100644 --- a/src/Compiler/Driver/CompilerOptions.fs +++ b/src/Compiler/Driver/CompilerOptions.fs @@ -255,7 +255,24 @@ module ResponseFile = with e -> Choice2Of2 e -let ParseCompilerOptions (collectOtherArgument: string -> unit, blocks: CompilerOptionBlock list, args) = +/// Emit a warning from command-line option processing, respecting --nowarn and warn-level +/// settings already accumulated on `tcConfigB`. +/// +/// Background: warnings emitted during option parsing are typically captured by a +/// `CapturingDiagnosticsLogger` and filtered at commit time. This helper is a second, +/// local line of defense so that adding a new `warning` callsite in this file cannot +/// silently bypass --nowarn even if the commit-time wrapper is missing. +let internal warningCmdLine (tcConfigB: TcConfigBuilder) (exn: exn) = + let diagnostic = + PhasedDiagnostic.Create(exn, DiagnosticsThreadStatics.BuildPhase, FSharpDiagnosticSeverity.Warning) + + match diagnostic.AdjustSeverity(tcConfigB.diagnosticsOptions) with + | FSharpDiagnosticSeverity.Hidden -> () + | FSharpDiagnosticSeverity.Warning -> warning exn + | FSharpDiagnosticSeverity.Error -> errorR exn + | FSharpDiagnosticSeverity.Info -> informationalWarning exn + +let ParseCompilerOptions (tcConfigB: TcConfigBuilder, collectOtherArgument: string -> unit, blocks: CompilerOptionBlock list, args) = use _ = UseBuildPhase BuildPhase.Parameter let specs = List.collect GetOptionsOfBlock blocks @@ -364,7 +381,7 @@ let ParseCompilerOptions (collectOtherArgument: string -> unit, blocks: Compiler let reportDeprecatedOption errOpt = match errOpt with - | Some e -> warning e + | Some e -> warningCmdLine tcConfigB e | None -> () let rec attempt l = @@ -1462,7 +1479,7 @@ let testFlag tcConfigB = #if DEBUG | "ShowParserStackOnParseError" -> showParserStackOnParseError <- true #endif - | str -> warning (Error(FSComp.SR.optsUnknownArgumentToTheTestSwitch str, rangeCmdArgs))), + | str -> warningCmdLine tcConfigB (Error(FSComp.SR.optsUnknownArgumentToTheTestSwitch str, rangeCmdArgs))), None, None ) @@ -2348,7 +2365,7 @@ let GetCoreFsiCompilerOptions (tcConfigB: TcConfigBuilder) = ) ] -let CheckAndReportSourceFileDuplicates (sourceFiles: ResizeArray) = +let CheckAndReportSourceFileDuplicates (tcConfigB: TcConfigBuilder) (sourceFiles: ResizeArray) = let visited = Dictionary.newWithSize (sourceFiles.Count * 2) let count = sourceFiles.Count @@ -2359,7 +2376,7 @@ let CheckAndReportSourceFileDuplicates (sourceFiles: ResizeArray) = match visited.TryGetValue source with | true, duplicatePosition -> - warning (Error(FSComp.SR.buildDuplicateFile (source, i + 1, count, duplicatePosition + 1, count), range0)) + warningCmdLine tcConfigB (Error(FSComp.SR.buildDuplicateFile (source, i + 1, count, duplicatePosition + 1, count), range0)) | false, _ -> visited.Add(source, i) yield source @@ -2373,8 +2390,8 @@ let ApplyCommandLineArgs (tcConfigB: TcConfigBuilder, sourceFiles: string list, if not (FileSystemUtils.isDll name) then sourceFilesAcc.Add name - ParseCompilerOptions(collect, GetCoreServiceCompilerOptions tcConfigB, argv) - sourceFilesAcc |> CheckAndReportSourceFileDuplicates + ParseCompilerOptions(tcConfigB, collect, GetCoreServiceCompilerOptions tcConfigB, argv) + sourceFilesAcc |> CheckAndReportSourceFileDuplicates tcConfigB with RecoverableException e -> errorRecovery e range0 sourceFiles diff --git a/src/Compiler/Driver/CompilerOptions.fsi b/src/Compiler/Driver/CompilerOptions.fsi index 7baefaa5aa1..26da7528c8b 100644 --- a/src/Compiler/Driver/CompilerOptions.fsi +++ b/src/Compiler/Driver/CompilerOptions.fsi @@ -50,7 +50,7 @@ val DumpCompilerOptionBlocks: CompilerOptionBlock list -> unit // for QA val FilterCompilerOptionBlock: (CompilerOption -> bool) -> CompilerOptionBlock -> CompilerOptionBlock /// Parse and process a set of compiler options -val ParseCompilerOptions: (string -> unit) * CompilerOptionBlock list * string list -> unit +val ParseCompilerOptions: TcConfigBuilder * (string -> unit) * CompilerOptionBlock list * string list -> unit val GetBannerText: tcConfigB: TcConfigBuilder -> string @@ -66,7 +66,7 @@ val GetCoreFsiCompilerOptions: TcConfigBuilder -> CompilerOptionBlock list val GetCoreServiceCompilerOptions: TcConfigBuilder -> CompilerOptionBlock list -val CheckAndReportSourceFileDuplicates: ResizeArray -> string list +val CheckAndReportSourceFileDuplicates: tcConfigB: TcConfigBuilder -> ResizeArray -> string list /// Apply args to TcConfigBuilder and return new list of source files val ApplyCommandLineArgs: tcConfigB: TcConfigBuilder * sourceFiles: string list * argv: string list -> string list diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index 968d9075368..a0d582a85aa 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -125,8 +125,10 @@ type CapturingDiagnosticsLogger with /// command-line option parsing (e.g. FS0075 from internal/test-only flags). member x.CommitDelayedDiagnostics(diagnosticsLoggerProvider: IDiagnosticsLoggerProvider, tcConfigB: TcConfigBuilder, exiter) = let diagnosticsLogger = diagnosticsLoggerProvider.CreateLogger(tcConfigB, exiter) + let filteredLogger = GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger) + x.CommitDelayedDiagnostics filteredLogger /// The default DiagnosticsLogger implementation, reporting messages to the Console up to the maxerrors maximum @@ -273,7 +275,7 @@ let ProcessCommandLineFlags (tcConfigB: TcConfigBuilder, lcidFromCodePage, argv) let abbrevArgs = GetAbbrevFlagSet tcConfigB true // This is where flags are interpreted by the command line fsc.exe. - ParseCompilerOptions(collect, GetCoreFscCompilerOptions tcConfigB, List.tail (PostProcessCompilerArgs abbrevArgs argv)) + ParseCompilerOptions(tcConfigB, collect, GetCoreFscCompilerOptions tcConfigB, List.tail (PostProcessCompilerArgs abbrevArgs argv)) let inputFiles = List.rev inputFilesRef @@ -509,7 +511,7 @@ let main1 // Rather than start processing, just collect names, then process them. try let files = ProcessCommandLineFlags(tcConfigB, lcidFromCodePage, argv) - let files = CheckAndReportSourceFileDuplicates(ResizeArray.ofList files) + let files = CheckAndReportSourceFileDuplicates tcConfigB (ResizeArray.ofList files) AdjustForScriptCompile(tcConfigB, files, lexResourceManager, dependencyProvider) with e -> errorRecovery e rangeStartup diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 1ff957e77a8..ff9319a3436 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -1225,7 +1225,7 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s @ fsiUsageSuffix tcConfigB let abbrevArgs = GetAbbrevFlagSet tcConfigB false - ParseCompilerOptions(collect, fsiCompilerOptions, List.tail (PostProcessCompilerArgs abbrevArgs argv)) + ParseCompilerOptions(tcConfigB, collect, fsiCompilerOptions, List.tail (PostProcessCompilerArgs abbrevArgs argv)) with e -> stopProcessingRecovery e range0 failwithf "Error creating evaluation session: %A" e diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 9e0e32bd7e7..0445dd13698 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1300,7 +1300,7 @@ type internal BackgroundCompiler let applyCompilerOptions tcConfigB = let fsiCompilerOptions = GetCoreFsiCompilerOptions tcConfigB - ParseCompilerOptions(ignore, fsiCompilerOptions, Array.toList otherFlags) + ParseCompilerOptions(tcConfigB, ignore, fsiCompilerOptions, Array.toList otherFlags) let loadClosure = LoadClosure.ComputeClosureOfScriptText( diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 6e9c1ced079..338b21460fa 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -4025,7 +4025,7 @@ type FsiInteractiveChecker let applyCompilerOptions tcConfigB = let fsiCompilerOptions = CompilerOptions.GetCoreFsiCompilerOptions tcConfigB - CompilerOptions.ParseCompilerOptions(ignore, fsiCompilerOptions, []) + CompilerOptions.ParseCompilerOptions(tcConfigB, ignore, fsiCompilerOptions, []) let loadClosure = LoadClosure.ComputeClosureOfScriptText( diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 32854ee041c..0beb515c726 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -492,7 +492,7 @@ type internal TransparentCompiler let fsiCompilerOptions = GetCoreFsiCompilerOptions tcConfig try - ParseCompilerOptions(ignore, fsiCompilerOptions, otherOptions) + ParseCompilerOptions(tcConfig, ignore, fsiCompilerOptions, otherOptions) with | :? OperationCanceledException -> reraise () | exn -> errorRecovery exn range0 diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs index 922d0925f58..7dc0bb6fe70 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs @@ -31,3 +31,13 @@ module ``Nowarn for command-line option warnings`` = |> compile |> shouldSucceed + // FS1063: "Unknown --test argument: '%s'". Emitted from `testingAndQAFlags` + // when an unknown sub-flag is passed via --test:. Routed through `warningCmdLine`. + [] + let ``--nowarn 1063 suppresses FS1063 for --test unknown-arg`` () = + FSharp "module Module" + |> withNoWarn 1063 + |> withOptions ["--test:NoSuchTestFlag"] + |> compile + |> shouldSucceed + From 47a5be3b87b275f6213b2a17d1023f6f1f99207f Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 15 May 2026 17:16:27 +0200 Subject: [PATCH 05/11] Consolidate command-line nowarn tests into Theory and add FS3551 case Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Fsc/warn/nowarn_for_cmdline_warnings.fs | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs index 7dc0bb6fe70..efe9f9a05b1 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs @@ -3,41 +3,40 @@ namespace CompilerOptions.Fsc open Xunit +open FSharp.Test open FSharp.Test.Compiler /// Regression tests for https://github.com/dotnet/fsharp/issues/19576 /// /// Warnings emitted during command-line option parsing (e.g. FS0075 for internal/test-only -/// options, FS3211 for duplicate source files, the test-switch unknown-arg warning) must -/// honor `--nowarn:` just like any other compiler warning. +/// options, FS1063 for unknown --test sub-flags, FS3551 for duplicate source files) must +/// honor `--nowarn:` just like any other compiler warning. They are routed through the +/// local `warningCmdLine` helper which consults `tcConfigB.diagnosticsOptions`. module ``Nowarn for command-line option warnings`` = - // FS0075: "The command-line option '%s' is for test purposes only" - // Emitted by reportDeprecatedOption in CompilerOptions.fs when an InternalCommandLineOption - // is used (e.g. --extraoptimizationloops). - [] - let ``--nowarn 75 suppresses FS0075 for --extraoptimizationloops`` () = + // FS0075: "The command-line option '%s' is for test purposes only" — reportDeprecatedOption. + // FS1063: "Unknown --test argument: '%s'" — testingAndQAFlags. + [] + [] + [] + [] + let ``--nowarn suppresses command-line option warning`` (warnNumber: int) (option: string) = FSharp "module Module" - |> withNoWarn 75 - |> withOptions ["--extraoptimizationloops:1"] + |> withNoWarn warnNumber + |> withOptions [option] |> compile |> shouldSucceed + // FS3551: "The source file '%s' (at position %d/%d) already appeared in the compilation list ..." + // Emitted by CheckAndReportSourceFileDuplicates and routed through `warningCmdLine`. [] - let ``--nowarn 75 suppresses FS0075 for --typedtree`` () = - FSharp "module Module" - |> withNoWarn 75 - |> withOptions ["--typedtree"] - |> compile - |> shouldSucceed + let ``--nowarn 3551 suppresses duplicate source file warning`` () = + let file = SourceCodeFileKind.Fs({ FileName = "test.fs"; SourceText = Some """printfn "Hello" """ }) - // FS1063: "Unknown --test argument: '%s'". Emitted from `testingAndQAFlags` - // when an unknown sub-flag is passed via --test:. Routed through `warningCmdLine`. - [] - let ``--nowarn 1063 suppresses FS1063 for --test unknown-arg`` () = - FSharp "module Module" - |> withNoWarn 1063 - |> withOptions ["--test:NoSuchTestFlag"] + fsFromString file + |> FS + |> asExe + |> withAdditionalSourceFile file + |> withNoWarn 3551 |> compile |> shouldSucceed - From e79c141c5726e71df7711d6e18e82291181fa901 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 19 May 2026 11:54:13 +0200 Subject: [PATCH 06/11] Remove warningCmdLine; use commit-time filtering only; add missing tests The emit-time warningCmdLine helper was order-dependent (only worked when --nowarn preceded the triggering option) and introduced a regression with --warnaserror+ (premature abort via errorR incrementing ErrorCount before --nowarn could take effect). Replace with commit-time filtering via GetDiagnosticsLoggerFilteringByScopedNowarn at the two sites in fsc.fs where CapturingDiagnosticsLogger replays captured diagnostics. This correctly applies all --nowarn/--warnaserror settings accumulated during the full option parsing pass. Revert ParseCompilerOptions and CheckAndReportSourceFileDuplicates signatures to their original forms (no TcConfigBuilder parameter needed). Add missing tests: - Baseline tests verifying warnings ARE emitted without --nowarn - --warnaserror+ with --nowarn interaction test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Driver/CompilerOptions.fs | 31 +++---------- src/Compiler/Driver/CompilerOptions.fsi | 4 +- src/Compiler/Driver/fsc.fs | 23 +++------- src/Compiler/Interactive/fsi.fs | 2 +- src/Compiler/Service/BackgroundCompiler.fs | 2 +- src/Compiler/Service/FSharpCheckerResults.fs | 2 +- src/Compiler/Service/TransparentCompiler.fs | 2 +- .../Fsc/warn/nowarn_for_cmdline_warnings.fs | 43 ++++++++++++++----- 8 files changed, 52 insertions(+), 57 deletions(-) diff --git a/src/Compiler/Driver/CompilerOptions.fs b/src/Compiler/Driver/CompilerOptions.fs index 118b5fc446d..98cec17265c 100644 --- a/src/Compiler/Driver/CompilerOptions.fs +++ b/src/Compiler/Driver/CompilerOptions.fs @@ -255,24 +255,7 @@ module ResponseFile = with e -> Choice2Of2 e -/// Emit a warning from command-line option processing, respecting --nowarn and warn-level -/// settings already accumulated on `tcConfigB`. -/// -/// Background: warnings emitted during option parsing are typically captured by a -/// `CapturingDiagnosticsLogger` and filtered at commit time. This helper is a second, -/// local line of defense so that adding a new `warning` callsite in this file cannot -/// silently bypass --nowarn even if the commit-time wrapper is missing. -let internal warningCmdLine (tcConfigB: TcConfigBuilder) (exn: exn) = - let diagnostic = - PhasedDiagnostic.Create(exn, DiagnosticsThreadStatics.BuildPhase, FSharpDiagnosticSeverity.Warning) - - match diagnostic.AdjustSeverity(tcConfigB.diagnosticsOptions) with - | FSharpDiagnosticSeverity.Hidden -> () - | FSharpDiagnosticSeverity.Warning -> warning exn - | FSharpDiagnosticSeverity.Error -> errorR exn - | FSharpDiagnosticSeverity.Info -> informationalWarning exn - -let ParseCompilerOptions (tcConfigB: TcConfigBuilder, collectOtherArgument: string -> unit, blocks: CompilerOptionBlock list, args) = +let ParseCompilerOptions (collectOtherArgument: string -> unit, blocks: CompilerOptionBlock list, args) = use _ = UseBuildPhase BuildPhase.Parameter let specs = List.collect GetOptionsOfBlock blocks @@ -381,7 +364,7 @@ let ParseCompilerOptions (tcConfigB: TcConfigBuilder, collectOtherArgument: stri let reportDeprecatedOption errOpt = match errOpt with - | Some e -> warningCmdLine tcConfigB e + | Some e -> warning e | None -> () let rec attempt l = @@ -1479,7 +1462,7 @@ let testFlag tcConfigB = #if DEBUG | "ShowParserStackOnParseError" -> showParserStackOnParseError <- true #endif - | str -> warningCmdLine tcConfigB (Error(FSComp.SR.optsUnknownArgumentToTheTestSwitch str, rangeCmdArgs))), + | str -> warning (Error(FSComp.SR.optsUnknownArgumentToTheTestSwitch str, rangeCmdArgs))), None, None ) @@ -2365,7 +2348,7 @@ let GetCoreFsiCompilerOptions (tcConfigB: TcConfigBuilder) = ) ] -let CheckAndReportSourceFileDuplicates (tcConfigB: TcConfigBuilder) (sourceFiles: ResizeArray) = +let CheckAndReportSourceFileDuplicates (sourceFiles: ResizeArray) = let visited = Dictionary.newWithSize (sourceFiles.Count * 2) let count = sourceFiles.Count @@ -2376,7 +2359,7 @@ let CheckAndReportSourceFileDuplicates (tcConfigB: TcConfigBuilder) (sourceFiles match visited.TryGetValue source with | true, duplicatePosition -> - warningCmdLine tcConfigB (Error(FSComp.SR.buildDuplicateFile (source, i + 1, count, duplicatePosition + 1, count), range0)) + warning (Error(FSComp.SR.buildDuplicateFile (source, i + 1, count, duplicatePosition + 1, count), range0)) | false, _ -> visited.Add(source, i) yield source @@ -2390,8 +2373,8 @@ let ApplyCommandLineArgs (tcConfigB: TcConfigBuilder, sourceFiles: string list, if not (FileSystemUtils.isDll name) then sourceFilesAcc.Add name - ParseCompilerOptions(tcConfigB, collect, GetCoreServiceCompilerOptions tcConfigB, argv) - sourceFilesAcc |> CheckAndReportSourceFileDuplicates tcConfigB + ParseCompilerOptions(collect, GetCoreServiceCompilerOptions tcConfigB, argv) + sourceFilesAcc |> CheckAndReportSourceFileDuplicates with RecoverableException e -> errorRecovery e range0 sourceFiles diff --git a/src/Compiler/Driver/CompilerOptions.fsi b/src/Compiler/Driver/CompilerOptions.fsi index 26da7528c8b..7baefaa5aa1 100644 --- a/src/Compiler/Driver/CompilerOptions.fsi +++ b/src/Compiler/Driver/CompilerOptions.fsi @@ -50,7 +50,7 @@ val DumpCompilerOptionBlocks: CompilerOptionBlock list -> unit // for QA val FilterCompilerOptionBlock: (CompilerOption -> bool) -> CompilerOptionBlock -> CompilerOptionBlock /// Parse and process a set of compiler options -val ParseCompilerOptions: TcConfigBuilder * (string -> unit) * CompilerOptionBlock list * string list -> unit +val ParseCompilerOptions: (string -> unit) * CompilerOptionBlock list * string list -> unit val GetBannerText: tcConfigB: TcConfigBuilder -> string @@ -66,7 +66,7 @@ val GetCoreFsiCompilerOptions: TcConfigBuilder -> CompilerOptionBlock list val GetCoreServiceCompilerOptions: TcConfigBuilder -> CompilerOptionBlock list -val CheckAndReportSourceFileDuplicates: tcConfigB: TcConfigBuilder -> ResizeArray -> string list +val CheckAndReportSourceFileDuplicates: ResizeArray -> string list /// Apply args to TcConfigBuilder and return new list of source files val ApplyCommandLineArgs: tcConfigB: TcConfigBuilder * sourceFiles: string list * argv: string list -> string list diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index a0d582a85aa..f124f8f82dd 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -119,17 +119,10 @@ type IDiagnosticsLoggerProvider = type CapturingDiagnosticsLogger with - /// Commit the delayed diagnostics via a fresh temporary logger of the right kind. - /// Wraps the target logger with a filter so that --nowarn / --warnaserror / warn level - /// set on the command line are honored for diagnostics that were captured *during* - /// command-line option parsing (e.g. FS0075 from internal/test-only flags). + /// Commit the delayed diagnostics, filtering by --nowarn/--warnaserror accumulated during option parsing. member x.CommitDelayedDiagnostics(diagnosticsLoggerProvider: IDiagnosticsLoggerProvider, tcConfigB: TcConfigBuilder, exiter) = let diagnosticsLogger = diagnosticsLoggerProvider.CreateLogger(tcConfigB, exiter) - - let filteredLogger = - GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger) - - x.CommitDelayedDiagnostics filteredLogger + x.CommitDelayedDiagnostics(GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger)) /// The default DiagnosticsLogger implementation, reporting messages to the Console up to the maxerrors maximum type ConsoleLoggerProvider() = @@ -275,7 +268,7 @@ let ProcessCommandLineFlags (tcConfigB: TcConfigBuilder, lcidFromCodePage, argv) let abbrevArgs = GetAbbrevFlagSet tcConfigB true // This is where flags are interpreted by the command line fsc.exe. - ParseCompilerOptions(tcConfigB, collect, GetCoreFscCompilerOptions tcConfigB, List.tail (PostProcessCompilerArgs abbrevArgs argv)) + ParseCompilerOptions(collect, GetCoreFscCompilerOptions tcConfigB, List.tail (PostProcessCompilerArgs abbrevArgs argv)) let inputFiles = List.rev inputFilesRef @@ -511,7 +504,7 @@ let main1 // Rather than start processing, just collect names, then process them. try let files = ProcessCommandLineFlags(tcConfigB, lcidFromCodePage, argv) - let files = CheckAndReportSourceFileDuplicates tcConfigB (ResizeArray.ofList files) + let files = CheckAndReportSourceFileDuplicates(ResizeArray.ofList files) AdjustForScriptCompile(tcConfigB, files, lexResourceManager, dependencyProvider) with e -> errorRecovery e rangeStartup @@ -584,12 +577,10 @@ let main1 // Install the global error logger and never remove it. This logger does have all command-line flags considered. SetThreadDiagnosticsLoggerNoUnwind diagnosticsLogger - // Forward all errors from flags, filtering by command-line --nowarn/--warnaserror so that - // diagnostics captured during option parsing honor those switches (see issue #19576). - let delayedFlagsCommitLogger = + // Forward all errors from flags, filtering by --nowarn/--warnaserror. + delayForFlagsLogger.CommitDelayedDiagnostics( GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger) - - delayForFlagsLogger.CommitDelayedDiagnostics delayedFlagsCommitLogger + ) if not tcConfigB.continueAfterParseFailure then AbortOnError(diagnosticsLogger, exiter) diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index ff9319a3436..1ff957e77a8 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -1225,7 +1225,7 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s @ fsiUsageSuffix tcConfigB let abbrevArgs = GetAbbrevFlagSet tcConfigB false - ParseCompilerOptions(tcConfigB, collect, fsiCompilerOptions, List.tail (PostProcessCompilerArgs abbrevArgs argv)) + ParseCompilerOptions(collect, fsiCompilerOptions, List.tail (PostProcessCompilerArgs abbrevArgs argv)) with e -> stopProcessingRecovery e range0 failwithf "Error creating evaluation session: %A" e diff --git a/src/Compiler/Service/BackgroundCompiler.fs b/src/Compiler/Service/BackgroundCompiler.fs index 0445dd13698..9e0e32bd7e7 100644 --- a/src/Compiler/Service/BackgroundCompiler.fs +++ b/src/Compiler/Service/BackgroundCompiler.fs @@ -1300,7 +1300,7 @@ type internal BackgroundCompiler let applyCompilerOptions tcConfigB = let fsiCompilerOptions = GetCoreFsiCompilerOptions tcConfigB - ParseCompilerOptions(tcConfigB, ignore, fsiCompilerOptions, Array.toList otherFlags) + ParseCompilerOptions(ignore, fsiCompilerOptions, Array.toList otherFlags) let loadClosure = LoadClosure.ComputeClosureOfScriptText( diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 338b21460fa..6e9c1ced079 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -4025,7 +4025,7 @@ type FsiInteractiveChecker let applyCompilerOptions tcConfigB = let fsiCompilerOptions = CompilerOptions.GetCoreFsiCompilerOptions tcConfigB - CompilerOptions.ParseCompilerOptions(tcConfigB, ignore, fsiCompilerOptions, []) + CompilerOptions.ParseCompilerOptions(ignore, fsiCompilerOptions, []) let loadClosure = LoadClosure.ComputeClosureOfScriptText( diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 0beb515c726..32854ee041c 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -492,7 +492,7 @@ type internal TransparentCompiler let fsiCompilerOptions = GetCoreFsiCompilerOptions tcConfig try - ParseCompilerOptions(tcConfig, ignore, fsiCompilerOptions, otherOptions) + ParseCompilerOptions(ignore, fsiCompilerOptions, otherOptions) with | :? OperationCanceledException -> reraise () | exn -> errorRecovery exn range0 diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs index efe9f9a05b1..41ee30d7886 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs @@ -7,31 +7,43 @@ open FSharp.Test open FSharp.Test.Compiler /// Regression tests for https://github.com/dotnet/fsharp/issues/19576 -/// -/// Warnings emitted during command-line option parsing (e.g. FS0075 for internal/test-only -/// options, FS1063 for unknown --test sub-flags, FS3551 for duplicate source files) must -/// honor `--nowarn:` just like any other compiler warning. They are routed through the -/// local `warningCmdLine` helper which consults `tcConfigB.diagnosticsOptions`. module ``Nowarn for command-line option warnings`` = - // FS0075: "The command-line option '%s' is for test purposes only" — reportDeprecatedOption. - // FS1063: "Unknown --test argument: '%s'" — testingAndQAFlags. + // -- Baseline: warnings ARE emitted without --nowarn ---------------------------- + + [] [] [] [] + let ``command-line option warning is emitted`` (warnNumber: int) (option: string) = + FSharp "module Module" + |> withOptions [ option ] + |> ignoreWarnings + |> compile + |> shouldSucceed + |> withWarningCode warnNumber + |> ignore + + // -- --nowarn suppresses the warning ------------------------------------------- + [] + [] + [] + [] let ``--nowarn suppresses command-line option warning`` (warnNumber: int) (option: string) = FSharp "module Module" |> withNoWarn warnNumber - |> withOptions [option] + |> withOptions [ option ] |> compile |> shouldSucceed - // FS3551: "The source file '%s' (at position %d/%d) already appeared in the compilation list ..." - // Emitted by CheckAndReportSourceFileDuplicates and routed through `warningCmdLine`. [] let ``--nowarn 3551 suppresses duplicate source file warning`` () = - let file = SourceCodeFileKind.Fs({ FileName = "test.fs"; SourceText = Some """printfn "Hello" """ }) + let file = + SourceCodeFileKind.Fs( + { FileName = "test.fs" + SourceText = Some """printfn "Hello" """ } + ) fsFromString file |> FS @@ -40,3 +52,12 @@ module ``Nowarn for command-line option warnings`` = |> withNoWarn 3551 |> compile |> shouldSucceed + + // -- --warnaserror interaction -------------------------------------------------- + + [] + let ``--warnaserror+ with --nowarn suppresses rather than errors`` () = + FSharp "module Module" + |> withOptions [ "--warnaserror+"; "--extraoptimizationloops:1"; "--nowarn:75" ] + |> compile + |> shouldSucceed From 5e73d2c8b52d6394dac5b50a93d35f2fe5398ea8 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 20 May 2026 14:52:33 +0200 Subject: [PATCH 07/11] Close test gaps: reverse-order, warnaserror baselines, specific warnaserror semantics Add tests identified by multi-model adversarial review: - Reverse-order test (--option before --nowarn) proves commit-time filtering works regardless of argument ordering - --warnaserror+ baseline proving warning is promoted to error - --warnaserror+ with --nowarn proving nowarn takes precedence (global) - --warnaserror:75 with --nowarn:75 documenting that specific warnaserror wins over --nowarn (AdjustSeverity uses localNowarn for specific, warnOff for global) 13 tests total, 0 duplication of test source code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Fsc/warn/nowarn_for_cmdline_warnings.fs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs index 41ee30d7886..75c98067f99 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs @@ -37,6 +37,16 @@ module ``Nowarn for command-line option warnings`` = |> compile |> shouldSucceed + [] + [] + [] + [] + let ``--nowarn after triggering option still suppresses`` (warnNumber: int) (option: string) = + FSharp "module Module" + |> withOptions [ option; $"--nowarn:{warnNumber}" ] + |> compile + |> shouldSucceed + [] let ``--nowarn 3551 suppresses duplicate source file warning`` () = let file = @@ -55,9 +65,27 @@ module ``Nowarn for command-line option warnings`` = // -- --warnaserror interaction -------------------------------------------------- + [] + let ``--warnaserror+ promotes command-line option warning to error`` () = + FSharp "module Module" + |> withOptions [ "--warnaserror+"; "--extraoptimizationloops:1" ] + |> compile + |> shouldFail + |> withErrorCode 75 + |> ignore + [] let ``--warnaserror+ with --nowarn suppresses rather than errors`` () = FSharp "module Module" |> withOptions [ "--warnaserror+"; "--extraoptimizationloops:1"; "--nowarn:75" ] |> compile |> shouldSucceed + + [] + let ``--warnaserror 75 with --nowarn 75 still errors because specific warnaserror wins`` () = + FSharp "module Module" + |> withOptions [ "--warnaserror:75"; "--extraoptimizationloops:1"; "--nowarn:75" ] + |> compile + |> shouldFail + |> withErrorCode 75 + |> ignore From 3b93fdf4cace65b4637bab7211df21bf6ed9b966 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 20 May 2026 15:28:55 +0200 Subject: [PATCH 08/11] Add mixed-code selectivity test for --nowarn precision Triggers both FS0075 and FS1063 simultaneously, suppresses only FS0075 via --nowarn:75, and asserts FS1063 still fires. Catches regressions where filtering accidentally drops all command-line warnings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Fsc/warn/nowarn_for_cmdline_warnings.fs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs index 75c98067f99..312bec73972 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs @@ -47,6 +47,16 @@ module ``Nowarn for command-line option warnings`` = |> compile |> shouldSucceed + [] + let ``--nowarn suppresses only the targeted warning`` () = + FSharp "module Module" + |> withOptions [ "--extraoptimizationloops:1"; "--test:NoSuchTestFlag"; "--nowarn:75" ] + |> ignoreWarnings + |> compile + |> shouldSucceed + |> withWarningCode 1063 + |> ignore + [] let ``--nowarn 3551 suppresses duplicate source file warning`` () = let file = From 7d93ad93c7f430dc05d40490dad52d1194b2c077 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 21 May 2026 11:16:26 +0200 Subject: [PATCH 09/11] Fix case-sensitivity: track test file under fsc/ (lowercase) for Linux CI The file was tracked by git as CompilerOptions/Fsc/warn/ (uppercase) while the fsproj and all sibling files use lowercase fsc/. On case-sensitive Linux this caused FS0225 'source file not found'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../{Fsc => fsc}/warn/nowarn_for_cmdline_warnings.fs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/FSharp.Compiler.ComponentTests/CompilerOptions/{Fsc => fsc}/warn/nowarn_for_cmdline_warnings.fs (100%) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs similarity index 100% rename from tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/warn/nowarn_for_cmdline_warnings.fs rename to tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs From 6a88603b1d6137c6cfa8eb19311b2a47f3e381e1 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 21 May 2026 11:26:23 +0200 Subject: [PATCH 10/11] Address review: remove slop comments, add order-independence test - Remove type annotation restating what inference provides - Replace code-restating comments with WHY-comments - Add test: --nowarn before --warnaserror+ still suppresses Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Driver/fsc.fs | 7 ++++--- .../fsc/warn/nowarn_for_cmdline_warnings.fs | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index f124f8f82dd..5bc73b4815f 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -119,8 +119,9 @@ type IDiagnosticsLoggerProvider = type CapturingDiagnosticsLogger with - /// Commit the delayed diagnostics, filtering by --nowarn/--warnaserror accumulated during option parsing. - member x.CommitDelayedDiagnostics(diagnosticsLoggerProvider: IDiagnosticsLoggerProvider, tcConfigB: TcConfigBuilder, exiter) = + /// Commit the delayed diagnostics through a nowarn-filtering logger so that + /// options parsed after the warning-emitting option can still suppress it. + member x.CommitDelayedDiagnostics(diagnosticsLoggerProvider: IDiagnosticsLoggerProvider, tcConfigB, exiter) = let diagnosticsLogger = diagnosticsLoggerProvider.CreateLogger(tcConfigB, exiter) x.CommitDelayedDiagnostics(GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger)) @@ -577,7 +578,7 @@ let main1 // Install the global error logger and never remove it. This logger does have all command-line flags considered. SetThreadDiagnosticsLoggerNoUnwind diagnosticsLogger - // Forward all errors from flags, filtering by --nowarn/--warnaserror. + // Replay delayed flag diagnostics through nowarn/warnaserror filtering. delayForFlagsLogger.CommitDelayedDiagnostics( GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger) ) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs index 312bec73972..89895af1335 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs @@ -91,6 +91,13 @@ module ``Nowarn for command-line option warnings`` = |> compile |> shouldSucceed + [] + let ``--nowarn before --warnaserror+ still suppresses`` () = + FSharp "module Module" + |> withOptions [ "--nowarn:75"; "--warnaserror+"; "--extraoptimizationloops:1" ] + |> compile + |> shouldSucceed + [] let ``--warnaserror 75 with --nowarn 75 still errors because specific warnaserror wins`` () = FSharp "module Module" From b214a496e04463a9aa4d04fb6210400467f7a9a4 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 21 May 2026 11:26:36 +0200 Subject: [PATCH 11/11] Address review: trim duplicate tests, remove slop comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove mechanical test duplicates (15→7): drop Theory×3 testing same warn number with different triggers, drop duplicate withNoWarn vs raw --nowarn:N tests, drop redundant order-independence test. Move release notes to 11.0.100.md (VNEXT). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../.FSharp.Compiler.Service/11.0.100.md | 1 + src/Compiler/Driver/fsc.fs | 4 +- .../fsc/warn/nowarn_for_cmdline_warnings.fs | 44 +++---------------- 3 files changed, 9 insertions(+), 40 deletions(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index a26f7e77ebe..7d4162f804e 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -1,5 +1,6 @@ ### Fixed +* Honor `--nowarn` and `--warnaserror` for warnings emitted during command-line option parsing ([Issue #19576](https://github.com/dotnet/fsharp/issues/19576), [PR #19776](https://github.com/dotnet/fsharp/pull/19776)) * Fix `[]` prefix attributes being silently dropped on class members, and fix false-positive `AllowMultiple=false` errors when `[]` and `[]` are applied to the same binding. ([Issue #17904](https://github.com/dotnet/fsharp/issues/17904), [Issue #19020](https://github.com/dotnet/fsharp/issues/19020), [PR #19738](https://github.com/dotnet/fsharp/pull/19738)) * Fix attributes on return type of unparenthesized tuple methods being silently dropped from IL. ([Issue #462](https://github.com/dotnet/fsharp/issues/462), [PR #19714](https://github.com/dotnet/fsharp/pull/19714)) * Fix internal error FS0073 "Undefined or unsolved type variable" in IlxGen when nested inline SRTP functions with multiple overloads leave unsolved typars in the non-witness codegen path. ([Issue #19709](https://github.com/dotnet/fsharp/issues/19709), [PR #19710](https://github.com/dotnet/fsharp/pull/19710)) diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index 5bc73b4815f..1912f13ff71 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -119,8 +119,6 @@ type IDiagnosticsLoggerProvider = type CapturingDiagnosticsLogger with - /// Commit the delayed diagnostics through a nowarn-filtering logger so that - /// options parsed after the warning-emitting option can still suppress it. member x.CommitDelayedDiagnostics(diagnosticsLoggerProvider: IDiagnosticsLoggerProvider, tcConfigB, exiter) = let diagnosticsLogger = diagnosticsLoggerProvider.CreateLogger(tcConfigB, exiter) x.CommitDelayedDiagnostics(GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger)) @@ -578,7 +576,7 @@ let main1 // Install the global error logger and never remove it. This logger does have all command-line flags considered. SetThreadDiagnosticsLoggerNoUnwind diagnosticsLogger - // Replay delayed flag diagnostics through nowarn/warnaserror filtering. + // Forward all errors from flags delayForFlagsLogger.CommitDelayedDiagnostics( GetDiagnosticsLoggerFilteringByScopedNowarn(tcConfigB.diagnosticsOptions, diagnosticsLogger) ) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs index 89895af1335..b6855e188db 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warn/nowarn_for_cmdline_warnings.fs @@ -9,41 +9,20 @@ open FSharp.Test.Compiler /// Regression tests for https://github.com/dotnet/fsharp/issues/19576 module ``Nowarn for command-line option warnings`` = - // -- Baseline: warnings ARE emitted without --nowarn ---------------------------- - - [] - [] - [] - [] - let ``command-line option warning is emitted`` (warnNumber: int) (option: string) = + [] + let ``command-line option warning is emitted`` () = FSharp "module Module" - |> withOptions [ option ] + |> withOptions [ "--extraoptimizationloops:1" ] |> ignoreWarnings |> compile |> shouldSucceed - |> withWarningCode warnNumber + |> withWarningCode 75 |> ignore - // -- --nowarn suppresses the warning ------------------------------------------- - - [] - [] - [] - [] - let ``--nowarn suppresses command-line option warning`` (warnNumber: int) (option: string) = - FSharp "module Module" - |> withNoWarn warnNumber - |> withOptions [ option ] - |> compile - |> shouldSucceed - - [] - [] - [] - [] - let ``--nowarn after triggering option still suppresses`` (warnNumber: int) (option: string) = + [] + let ``--nowarn suppresses command-line option warning`` () = FSharp "module Module" - |> withOptions [ option; $"--nowarn:{warnNumber}" ] + |> withOptions [ "--extraoptimizationloops:1"; "--nowarn:75" ] |> compile |> shouldSucceed @@ -73,8 +52,6 @@ module ``Nowarn for command-line option warnings`` = |> compile |> shouldSucceed - // -- --warnaserror interaction -------------------------------------------------- - [] let ``--warnaserror+ promotes command-line option warning to error`` () = FSharp "module Module" @@ -91,13 +68,6 @@ module ``Nowarn for command-line option warnings`` = |> compile |> shouldSucceed - [] - let ``--nowarn before --warnaserror+ still suppresses`` () = - FSharp "module Module" - |> withOptions [ "--nowarn:75"; "--warnaserror+"; "--extraoptimizationloops:1" ] - |> compile - |> shouldSucceed - [] let ``--warnaserror 75 with --nowarn 75 still errors because specific warnaserror wins`` () = FSharp "module Module"