Skip to content
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* 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 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))
Expand Down
11 changes: 10 additions & 1 deletion src/Compiler/Checking/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5522,7 +5522,16 @@ and TcStmtThatCantBeCtorBody (cenv: cenv) env tpenv synExpr =
and TcStmt (cenv: cenv) env tpenv synExpr =
let g = cenv.g
let expr, ty, tpenv = TcExprOfUnknownType cenv env tpenv synExpr
let m = synExpr.Range

// 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
let rec lastExprRange (e: SynExpr) =
match e with
| SynExpr.Sequential(expr2 = expr2) -> lastExprRange expr2
| _ -> e.Range

let m = lastExprRange synExpr
let wasUnit = UnifyUnitType cenv env m ty expr
if wasUnit then
expr, tpenv
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,63 @@ 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
[<Fact>]
let ``Warn On Last Expression In For Loop - int``() =
FSharp """
module ClassLibrary17

for i in 1 .. 10 do
printfn ""
printfn "" |> ignore
123
"""
|> typecheck
|> shouldFail
|> 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
[<Fact>]
let ``Warn On Last Expression In For Loop - string``() =
FSharp """
for i in 1 .. 10 do
printfn ""
"hello"
"""
|> typecheck
|> shouldFail
|> 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
[<Fact>]
let ``Warn On Last Expression In Integer For Loop``() =
FSharp """
for i = 1 to 10 do
printfn ""
42
"""
|> typecheck
|> shouldFail
|> 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
[<Fact>]
let ``Warn On Last Expression In While Loop - non-bool``() =
FSharp """
let mutable x = 0
while x < 1 do
printfn "unneeded"
x <- x + 1
123
"""
|> typecheck
|> shouldFail
|> 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'.")

[<Fact>]
let ``Warn If Possible Property Setter``() =
FSharp """
Expand Down