From 27403104a77272e512b2e9f84a6180774ec82c0f Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 4 Jun 2026 15:01:52 +0200 Subject: [PATCH 1/7] Address Eugene's feedback on PR #19504 - Walk through let/use bindings in addition to Sequential when locating the offending expression for warning 20. Fixes the case Eugene flagged: for _ in [] do let x = 1 x - Fix wrong issue link (#5735 -> #5418) in code comment, release notes, and test annotations. - Add regression tests for the let/use case (single + nested bindings). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../.FSharp.Compiler.Service/11.0.100.md | 3 +- .../Checking/Expressions/CheckExpressions.fs | 8 +++-- .../ErrorMessages/WarnExpressionTests.fs | 35 ++++++++++++++++--- 3 files changed, 38 insertions(+), 8 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 9509ac72a3a..c9ff307f2d2 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -16,8 +16,9 @@ * Fix DU case names matching IWSAM member names no longer cause duplicate property entries. (Issue [#14321](https://github.com/dotnet/fsharp/issues/14321), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) * Fix DefaultAugmentation(false) duplicate entry in method table. (Issue [#16565](https://github.com/dotnet/fsharp/issues/16565), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) * Fix abstract event accessors now have SpecialName flag. (Issue [#5834](https://github.com/dotnet/fsharp/issues/5834), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) -* Fix warning 20 ("expression is implicitly ignored") pointing at the wrong range when the last expression in a sequential block (e.g. inside `for`, `while` loops) is non-unit. The squiggle now correctly highlights only the offending expression. ([Issue #5735](https://github.com/dotnet/fsharp/issues/5735), [PR #19504](https://github.com/dotnet/fsharp/pull/19504)) +* Fix warning 20 ("expression is implicitly ignored") pointing at the wrong range when the last expression in a sequential block (e.g. inside `for`, `while` loops) is non-unit. The squiggle now correctly highlights only the offending expression. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418), [PR #19504](https://github.com/dotnet/fsharp/pull/19504)) * Fix missing "No implementation was given" error when F# class inherits from a C# class with `abstract override` members without providing an implementation. ([Issue #7776](https://github.com/dotnet/fsharp/issues/7776), [PR #19503](https://github.com/dotnet/fsharp/pull/19503)) +* Extend the warning 20 range fix to also walk through `let`/`use` bindings, so the squiggle lands on the offending expression when it is the body of a `let`/`use`. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418)) * Fix CLIEvent properties to be correctly recognized as events: `IsEvent` returns `true` and `XmlDocSig` uses `E:` prefix instead of `P:`. ([Issue #10273](https://github.com/dotnet/fsharp/issues/10273), [PR #18584](https://github.com/dotnet/fsharp/pull/18584)) * Fix extra sequence point at the end of match expressions. ([Issue #12052](https://github.com/dotnet/fsharp/issues/12052), [PR #19278](https://github.com/dotnet/fsharp/pull/19278)) * Fix wrong sequence point range for `return`/`yield`/`return!`/`yield!` inside computation expressions. ([Issue #19248](https://github.com/dotnet/fsharp/issues/19248), [PR #19278](https://github.com/dotnet/fsharp/pull/19278)) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index f6e0a61663f..e6d92917012 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -5582,12 +5582,14 @@ and TcStmt (cenv: cenv) env tpenv synExpr = let g = cenv.g let expr, ty, tpenv = TcExprOfUnknownType cenv env tpenv synExpr - // Use the range of the last expression in a sequential chain for warnings, - // so that "expression is ignored" diagnostics point at the offending expression - // rather than the entire sequential body. See https://github.com/dotnet/fsharp/issues/5735 + // Use the range of the last expression in a sequential chain (also walking through + // 'let'/'use' bindings) for warnings, so that "expression is ignored" diagnostics point + // at the offending expression rather than the entire sequential body. + // See https://github.com/dotnet/fsharp/issues/5418 let rec lastExprRange (e: SynExpr) = match e with | SynExpr.Sequential(expr2 = expr2) -> lastExprRange expr2 + | SynExpr.LetOrUse({ Body = body }) -> lastExprRange body | _ -> e.Range let m = lastExprRange synExpr diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs index bc68eb4d7d9..6a8e6f789d1 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs @@ -176,7 +176,7 @@ while x < 1 do |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 9, "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5735 + // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In For Loop - int``() = FSharp """ @@ -192,7 +192,7 @@ for i in 1 .. 10 do |> withSingleDiagnostic (Warning 20, Line 7, Col 5, Line 7, Col 8, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5735 + // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In For Loop - string``() = FSharp """ @@ -205,7 +205,7 @@ for i in 1 .. 10 do |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 12, "The result of this expression has type 'string' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5735 + // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In Integer For Loop``() = FSharp """ @@ -218,7 +218,7 @@ for i = 1 to 10 do |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 7, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5735 + // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In While Loop - non-bool``() = FSharp """ @@ -233,6 +233,33 @@ while x < 1 do |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 8, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + // https://github.com/dotnet/fsharp/issues/5418 + [] + let ``Warn On Last Expression In For Loop - non-unit after let binding``() = + FSharp """ +for _ in [] do + let x = 1 + x + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 6, + "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + + // https://github.com/dotnet/fsharp/issues/5418 + [] + let ``Warn On Last Expression In For Loop - non-unit after nested let bindings``() = + FSharp """ +for _ in 1 .. 3 do + let a = 1 + let b = 2 + a + b + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 5, Col 5, Line 5, Col 10, + "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + [] let ``Warn If Possible Property Setter``() = FSharp """ From 3330e8c0e36e1dca2a4a90e7cf1a28e0769b2e81 Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 12 Jun 2026 12:34:23 +0200 Subject: [PATCH 2/7] Address review feedback: remove redundant comments, add use/CE tests, add PR link - Remove issue URL references from code comments and test methods (per auduchinok: redundant, commit links to PR with full context) - Add test for 'use' binding (non-unit after use) - Add test for computation expression after let binding - Add PR #19896 link to release notes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../.FSharp.Compiler.Service/11.0.100.md | 2 +- .../Checking/Expressions/CheckExpressions.fs | 1 - .../ErrorMessages/WarnExpressionTests.fs | 34 +++++++++++++++---- 3 files changed, 29 insertions(+), 8 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 c9ff307f2d2..5d3ffb667b7 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -18,7 +18,7 @@ * Fix abstract event accessors now have SpecialName flag. (Issue [#5834](https://github.com/dotnet/fsharp/issues/5834), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) * Fix warning 20 ("expression is implicitly ignored") pointing at the wrong range when the last expression in a sequential block (e.g. inside `for`, `while` loops) is non-unit. The squiggle now correctly highlights only the offending expression. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418), [PR #19504](https://github.com/dotnet/fsharp/pull/19504)) * Fix missing "No implementation was given" error when F# class inherits from a C# class with `abstract override` members without providing an implementation. ([Issue #7776](https://github.com/dotnet/fsharp/issues/7776), [PR #19503](https://github.com/dotnet/fsharp/pull/19503)) -* Extend the warning 20 range fix to also walk through `let`/`use` bindings, so the squiggle lands on the offending expression when it is the body of a `let`/`use`. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418)) +* Extend the warning 20 range fix to also walk through `let`/`use` bindings, so the squiggle lands on the offending expression when it is the body of a `let`/`use`. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418), [PR #19896](https://github.com/dotnet/fsharp/pull/19896)) * Fix CLIEvent properties to be correctly recognized as events: `IsEvent` returns `true` and `XmlDocSig` uses `E:` prefix instead of `P:`. ([Issue #10273](https://github.com/dotnet/fsharp/issues/10273), [PR #18584](https://github.com/dotnet/fsharp/pull/18584)) * Fix extra sequence point at the end of match expressions. ([Issue #12052](https://github.com/dotnet/fsharp/issues/12052), [PR #19278](https://github.com/dotnet/fsharp/pull/19278)) * Fix wrong sequence point range for `return`/`yield`/`return!`/`yield!` inside computation expressions. ([Issue #19248](https://github.com/dotnet/fsharp/issues/19248), [PR #19278](https://github.com/dotnet/fsharp/pull/19278)) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index e6d92917012..2ec35bca356 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -5585,7 +5585,6 @@ and TcStmt (cenv: cenv) env tpenv synExpr = // Use the range of the last expression in a sequential chain (also walking through // 'let'/'use' bindings) for warnings, so that "expression is ignored" diagnostics point // at the offending expression rather than the entire sequential body. - // See https://github.com/dotnet/fsharp/issues/5418 let rec lastExprRange (e: SynExpr) = match e with | SynExpr.Sequential(expr2 = expr2) -> lastExprRange expr2 diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs index 6a8e6f789d1..2e5b6c1c943 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs @@ -176,7 +176,6 @@ while x < 1 do |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 9, "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In For Loop - int``() = FSharp """ @@ -192,7 +191,6 @@ for i in 1 .. 10 do |> withSingleDiagnostic (Warning 20, Line 7, Col 5, Line 7, Col 8, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In For Loop - string``() = FSharp """ @@ -205,7 +203,6 @@ for i in 1 .. 10 do |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 12, "The result of this expression has type 'string' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In Integer For Loop``() = FSharp """ @@ -218,7 +215,6 @@ for i = 1 to 10 do |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 7, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In While Loop - non-bool``() = FSharp """ @@ -233,7 +229,6 @@ while x < 1 do |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 8, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In For Loop - non-unit after let binding``() = FSharp """ @@ -246,7 +241,6 @@ for _ in [] do |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 6, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") - // https://github.com/dotnet/fsharp/issues/5418 [] let ``Warn On Last Expression In For Loop - non-unit after nested let bindings``() = FSharp """ @@ -260,6 +254,34 @@ for _ in 1 .. 3 do |> withSingleDiagnostic (Warning 20, Line 5, Col 5, Line 5, Col 10, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + [] + let ``Warn On Last Expression In For Loop - non-unit after use binding``() = + FSharp """ +type D() = + interface System.IDisposable with + member _.Dispose() = () + member _.Value = 1 +for _ in [] do + use d = new D() + d.Value + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 8, Col 5, Line 8, Col 12, + "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + + [] + let ``Warn On Last Expression In For Loop - non-unit computation expression after let binding``() = + FSharp """ +for _ in 1 .. 3 do + let x = async { return 1 } + x + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 6, + "The result of this expression has type 'Async' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + [] let ``Warn If Possible Property Setter``() = FSharp """ From 3b7449e2ea68bf849e7e51617559396071594046 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 12 Jun 2026 12:42:20 +0200 Subject: [PATCH 3/7] Address Eugene's feedback: cut comment bloat, strengthen CE test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Checking/Expressions/CheckExpressions.fs | 3 --- .../ErrorMessages/WarnExpressionTests.fs | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index e4595597a13..70a3e933f38 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -5582,9 +5582,6 @@ and TcStmt (cenv: cenv) env tpenv synExpr = let g = cenv.g let expr, ty, tpenv = TcExprOfUnknownType cenv env tpenv synExpr - // Use the range of the last expression in a sequential chain (also walking through - // 'let'/'use' bindings) for warnings, so that "expression is ignored" diagnostics point - // at the offending expression rather than the entire sequential body. let rec lastExprRange (e: SynExpr) = match e with | SynExpr.Sequential(expr2 = expr2) -> lastExprRange expr2 diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs index 2e5b6c1c943..f331a4049fa 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs @@ -274,13 +274,13 @@ for _ in [] do let ``Warn On Last Expression In For Loop - non-unit computation expression after let binding``() = FSharp """ for _ in 1 .. 3 do - let x = async { return 1 } - x + let x = 1 + seq { yield x } """ |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 6, - "The result of this expression has type 'Async' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 20, + "The result of this expression has type 'int seq' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn If Possible Property Setter``() = From c9359719195120612c1b06677dc2ead0d386c556 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 12 Jun 2026 13:03:05 +0200 Subject: [PATCH 4/7] Drop duplicate release-notes entry; PR #19504's existing bullet already covers the user-facing fix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 3 +-- 1 file changed, 1 insertion(+), 2 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 cf6d70d746b..0ba56d79f13 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -20,9 +20,8 @@ * Fix DU case names matching IWSAM member names no longer cause duplicate property entries. (Issue [#14321](https://github.com/dotnet/fsharp/issues/14321), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) * Fix DefaultAugmentation(false) duplicate entry in method table. (Issue [#16565](https://github.com/dotnet/fsharp/issues/16565), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) * Fix abstract event accessors now have SpecialName flag. (Issue [#5834](https://github.com/dotnet/fsharp/issues/5834), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) -* Fix warning 20 ("expression is implicitly ignored") pointing at the wrong range when the last expression in a sequential block (e.g. inside `for`, `while` loops) is non-unit. The squiggle now correctly highlights only the offending expression. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418), [PR #19504](https://github.com/dotnet/fsharp/pull/19504)) +* Fix warning 20 ("expression is implicitly ignored") pointing at the wrong range when the last expression in a sequential block (e.g. inside `for`, `while` loops) is non-unit. The squiggle now correctly highlights only the offending expression. ([Issue #5735](https://github.com/dotnet/fsharp/issues/5735), [PR #19504](https://github.com/dotnet/fsharp/pull/19504)) * Fix missing "No implementation was given" error when F# class inherits from a C# class with `abstract override` members without providing an implementation. ([Issue #7776](https://github.com/dotnet/fsharp/issues/7776), [PR #19503](https://github.com/dotnet/fsharp/pull/19503)) -* Extend the warning 20 range fix to also walk through `let`/`use` bindings, so the squiggle lands on the offending expression when it is the body of a `let`/`use`. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418), [PR #19896](https://github.com/dotnet/fsharp/pull/19896)) * Fix CLIEvent properties to be correctly recognized as events: `IsEvent` returns `true` and `XmlDocSig` uses `E:` prefix instead of `P:`. ([Issue #10273](https://github.com/dotnet/fsharp/issues/10273), [PR #18584](https://github.com/dotnet/fsharp/pull/18584)) * Fix extra sequence point at the end of match expressions. ([Issue #12052](https://github.com/dotnet/fsharp/issues/12052), [PR #19278](https://github.com/dotnet/fsharp/pull/19278)) * Fix wrong sequence point range for `return`/`yield`/`return!`/`yield!` inside computation expressions. ([Issue #19248](https://github.com/dotnet/fsharp/issues/19248), [PR #19278](https://github.com/dotnet/fsharp/pull/19278)) From 19cd063a192e3f2a35d46eb6c0106a21bfa8bd1d Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Fri, 12 Jun 2026 13:57:41 +0200 Subject: [PATCH 5/7] Use async (real CE) in CE-case test, not seq Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ErrorMessages/WarnExpressionTests.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs index f331a4049fa..25b8145adb7 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs @@ -275,12 +275,12 @@ for _ in [] do FSharp """ for _ in 1 .. 3 do let x = 1 - seq { yield x } + async { return x } """ |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 20, - "The result of this expression has type 'int seq' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 23, + "The result of this expression has type 'Async' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn If Possible Property Setter``() = From 9dbaa2e7f880d238b8fd2b03c947d71135d78c8d Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 12 Jun 2026 14:12:41 +0200 Subject: [PATCH 6/7] Add release notes entry for PR #19896 The CI check requires a release notes entry referencing this PR's URL. Also corrects the issue reference from #5735 to #5418 in the existing warning 20 entry (per review feedback on PR #19504). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 3 ++- 1 file changed, 2 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 0ba56d79f13..a21f531c5b6 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -20,7 +20,8 @@ * Fix DU case names matching IWSAM member names no longer cause duplicate property entries. (Issue [#14321](https://github.com/dotnet/fsharp/issues/14321), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) * Fix DefaultAugmentation(false) duplicate entry in method table. (Issue [#16565](https://github.com/dotnet/fsharp/issues/16565), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) * Fix abstract event accessors now have SpecialName flag. (Issue [#5834](https://github.com/dotnet/fsharp/issues/5834), [PR #19341](https://github.com/dotnet/fsharp/pull/19341)) -* Fix warning 20 ("expression is implicitly ignored") pointing at the wrong range when the last expression in a sequential block (e.g. inside `for`, `while` loops) is non-unit. The squiggle now correctly highlights only the offending expression. ([Issue #5735](https://github.com/dotnet/fsharp/issues/5735), [PR #19504](https://github.com/dotnet/fsharp/pull/19504)) +* Fix warning 20 ("expression is implicitly ignored") pointing at the wrong range when the last expression in a sequential block (e.g. inside `for`, `while` loops) is non-unit. The squiggle now correctly highlights only the offending expression. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418), [PR #19504](https://github.com/dotnet/fsharp/pull/19504)) +* Extend the warning 20 range fix to also walk through `let`/`use` bindings, so the squiggle lands on the offending expression when it is the body of a `let`/`use`. ([Issue #5418](https://github.com/dotnet/fsharp/issues/5418), [PR #19896](https://github.com/dotnet/fsharp/pull/19896)) * Fix missing "No implementation was given" error when F# class inherits from a C# class with `abstract override` members without providing an implementation. ([Issue #7776](https://github.com/dotnet/fsharp/issues/7776), [PR #19503](https://github.com/dotnet/fsharp/pull/19503)) * Fix CLIEvent properties to be correctly recognized as events: `IsEvent` returns `true` and `XmlDocSig` uses `E:` prefix instead of `P:`. ([Issue #10273](https://github.com/dotnet/fsharp/issues/10273), [PR #18584](https://github.com/dotnet/fsharp/pull/18584)) * Fix extra sequence point at the end of match expressions. ([Issue #12052](https://github.com/dotnet/fsharp/issues/12052), [PR #19278](https://github.com/dotnet/fsharp/pull/19278)) From 0127569be566f59de051a5abfaffc354d904bba8 Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 12 Jun 2026 17:02:08 +0200 Subject: [PATCH 7/7] Replace misleading CE test with while-loop + let binding test The previous 'computation expression after let binding' test only used async { return x } as a non-unit value - it didn't exercise CE-specific syntax constructs. Replace with a while-loop test that genuinely exercises the TcStmt lastExprRange fix in a different loop context. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ErrorMessages/WarnExpressionTests.fs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs index 25b8145adb7..59b6877698c 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs @@ -271,16 +271,18 @@ for _ in [] do "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] - let ``Warn On Last Expression In For Loop - non-unit computation expression after let binding``() = + let ``Warn On Last Expression In While Loop - non-unit after let binding``() = FSharp """ -for _ in 1 .. 3 do - let x = 1 - async { return x } +let mutable x = 0 +while x < 3 do + let y = x + 1 + x <- y + y """ |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 20, Line 4, Col 5, Line 4, Col 23, - "The result of this expression has type 'Async' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 6, + "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn If Possible Property Setter``() =