From 2ee043161c28a7d74bea0e96b9b63ce1e4ff9f20 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 25 May 2026 21:20:15 +0200 Subject: [PATCH 1/2] Fix FS1182 not reported for unused let functions in class types (#13849) Removed the 'not v.IsCompiledAsTopLevel' guard in the local 'reportIfUnused' helper inside CheckIncrementalClasses.IncrClassReprInfo.ChooseRepresentation. Class-let function bindings are promoted to compiler-generated members, which sets IsCompiledAsTopLevel=true and previously silently suppressed FS1182. The helper is local to class-binding representation choice, so this change cannot introduce module-level false positives. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../.FSharp.Compiler.Service/11.0.100.md | 1 + .../Checking/CheckIncrementalClasses.fs | 2 +- .../CompilerOptions/fsc/warnon/warnon.fs | 151 ++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) 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 7d4162f804..c88f7aa95b 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 +* Fix missing FS1182 ("unused binding") warning for unused `let` function bindings inside class types. ([Issue #13849](https://github.com/dotnet/fsharp/issues/13849)) * 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)) diff --git a/src/Compiler/Checking/CheckIncrementalClasses.fs b/src/Compiler/Checking/CheckIncrementalClasses.fs index 846aa5f908..93a7cf647c 100644 --- a/src/Compiler/Checking/CheckIncrementalClasses.fs +++ b/src/Compiler/Checking/CheckIncrementalClasses.fs @@ -327,7 +327,7 @@ type IncrClassReprInfo = nm, takenFieldNames.Add nm let reportIfUnused() = - if not v.HasBeenReferenced && not v.IsCompiledAsTopLevel && not (v.DisplayName.StartsWithOrdinal("_")) && not v.IsCompilerGenerated then + if not v.HasBeenReferenced && not (v.DisplayName.StartsWithOrdinal("_")) && not v.IsCompilerGenerated then warning (Error(FSComp.SR.chkUnusedValue(v.DisplayName), v.Range)) let repr = diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warnon/warnon.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warnon/warnon.fs index c726ecbde8..bdae02a2c4 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warnon/warnon.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warnon/warnon.fs @@ -144,3 +144,154 @@ type System.Int32 with |> compile |> shouldSucceed |> ignore + + // ------------------------------------------------------------------------- + // FS1182 tests for `let`-bound functions inside class types (issue #13849). + // Today, FS1182 fires for unused `let`-bound values inside classes, but not + // for unused `let`-bound functions inside classes. These tests pin the + // expected post-fix behavior. The tests marked "RED" must FAIL today and + // pass once the fix lands; the tests marked "GUARD" must already pass. + // ------------------------------------------------------------------------- + + // RED: primary repro from the issue. + [] + let ``Unused let function in class reports FS1182`` () = + FSharp """ +module M +type T() = + let f _ = () +""" + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> withWarningCode 1182 + |> withDiagnosticMessageMatches "The value 'f' is unused" + |> ignore + + // RED: variants of unused let-function in classes. + // Note: the recursive variant (`let rec f x = ... f (x-1)`) is intentionally + // omitted from this Theory because F# treats the self-reference inside a + // `rec` binding as a use, so it does not warn; that behavior is unchanged by + // the fix and is covered separately below as a negative regression guard. + [] + [] + [] + [] + let ``Unused let function variants in class report FS1182`` (source: string) = + FSharp source + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> withWarningCode 1182 + |> withDiagnosticMessageMatches "The value 'f' is unused" + |> ignore + + // GUARD: a recursive let-function in a class that only references itself. + // F# considers the self-reference inside `let rec` as a use, so no FS1182 + // is expected (today and after the fix). Documented as a regression guard. + [] + let ``Recursive let function in class with only self-reference does NOT report FS1182`` () = + FSharp """ +module M +type T() = + let rec f x = if x = 0 then 1 else f (x-1) +""" + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> shouldSucceed + |> withDiagnostics [] + |> ignore + + // GUARD: used let-function in a class must not warn (today and after fix). + [] + let ``Used let function in class does NOT report FS1182`` () = + FSharp """ +module M +type T() = + let f x = x + 1 + member _.GetValue() = f 42 +""" + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> shouldSucceed + |> withDiagnostics [] + |> ignore + + // GUARD: unused let-value in a class already warns today. + [] + let ``Unused let value in class still reports FS1182`` () = + FSharp """ +module M +type T() = + let x = 42 +""" + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> withWarningCode 1182 + |> withDiagnosticMessageMatches "The value 'x' is unused" + |> ignore + + // GUARD: module-level unused functions must NOT warn (they are public surface). + // This guards against accidentally widening the fix beyond class-let bindings. + [] + let ``Module-level unused function does not get new false positive`` () = + FSharp """ +module M +let f x = x + 1 +""" + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> shouldSucceed + |> withDiagnostics [] + |> ignore + + // GUARD: underscore-prefixed unused let-function in a class must NOT warn. + [] + let ``Underscore-prefixed let function in class does NOT report FS1182`` () = + FSharp """ +module M +type T() = + let _f x = x + 1 +""" + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> shouldSucceed + |> withDiagnostics [] + |> ignore + + // RED: mixed used/unused let-functions in a class - only unused one warns. + [] + let ``Mixed used and unused let functions in class`` () = + FSharp """ +module M +type T() = + let used x = x + 1 + let unused x = x * 2 + member _.GetValue() = used 42 +""" + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> withWarningCode 1182 + |> withDiagnosticMessageMatches "The value 'unused' is unused" + |> ignore + + // RED: static let function in a class - same code path with isStatic=true. + [] + let ``Unused static let function in class reports FS1182`` () = + FSharp """ +module M +type T() = + static let f x = x + 1 +""" + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> withWarningCode 1182 + |> withDiagnosticMessageMatches "The value 'f' is unused" + |> ignore From ae3c3485a82e65b08182da70207cefb64d93cc51 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Mon, 25 May 2026 22:01:24 +0200 Subject: [PATCH 2/2] Apply remaining changes --- .executor-pid | 1 + 1 file changed, 1 insertion(+) create mode 100644 .executor-pid diff --git a/.executor-pid b/.executor-pid new file mode 100644 index 0000000000..b45afb1b21 --- /dev/null +++ b/.executor-pid @@ -0,0 +1 @@ +13284 \ No newline at end of file