Skip to content
Open
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
2 changes: 2 additions & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
* Fix parallel compilation of scripts ([PR #19649](https://github.com/dotnet/fsharp/pull/19649))
* Fix parser recovery, name resolution, and code completion for unfinished enum patterns ([PR #19708](https://github.com/dotnet/fsharp/pull/19708))
* Parser: fix unexpected diagnostics in debug builds, improve error messages ([PR #19730](https://github.com/dotnet/fsharp/pull/19730))
* Parser: recover on unfinished if and binary expressions
([PR #19724](https://github.com/dotnet/fsharp/pull/19724))

### Added

Expand Down
102 changes: 100 additions & 2 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ let parse_error_rich = Some(fun (ctxt: ParseErrorContext<_>) ->

/* start with lowest */

%nonassoc prec_atompat_pathop_error
%nonassoc prec_recover /* the lowest precedence to use as the last chance recovery */
%nonassoc prec_args_error /* less than RPAREN */
%nonassoc prec_atomexpr_lparen_error /* less than RPAREN */

Expand Down Expand Up @@ -4335,6 +4335,16 @@ declExpr:
let trivia = { IfKeyword = mIf; IsElif = false; ThenKeyword = mThen; ElseKeyword = None; IfToThenRange = m }
SynExpr.IfThenElse($2, arbExpr ("if1", mThen), None, spIfToThen, true, m, trivia) }

| IF declExpr %prec prec_recover
{ let mIf = rhs parseState 1
errorR (Error(FSComp.SR.parsIncompleteIf (), mIf))
let ifExpr = $2
let mThen = ifExpr.Range.EndRange
let m = unionRanges mIf mThen
let spIfToThen = DebugPointAtBinding.Yes m
let trivia = { IfKeyword = mIf; IsElif = false; ThenKeyword = mThen; ElseKeyword = None; IfToThenRange = m }
SynExpr.IfThenElse($2, arbExpr ("if4", mThen), None, spIfToThen, true, m, trivia) }

| IF recover %prec expr_if
{ errorR (Error(FSComp.SR.parsIncompleteIf (), rhs parseState 1))
let m = rhs parseState 1
Expand Down Expand Up @@ -4674,6 +4684,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "in")
mkSynInfix mOp $1 "@in" (arbExpr ("declExprInfixJoinIn", mOp.EndRange)) }

| declExpr JOIN_IN %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "in")
mkSynInfix mOp $1 "@in" (arbExpr ("declExprInfixJoinIn2", mOp.EndRange)) }

| declExpr BAR_BAR declExpr
{ mkSynInfix (rhs parseState 2) $1 "||" $3 }

Expand All @@ -4682,6 +4697,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "||")
mkSynInfix mOp $1 "||" (arbExpr ("declExprInfixBarBar", mOp.EndRange)) }

| declExpr BAR_BAR %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "||")
mkSynInfix mOp $1 "||" (arbExpr ("declExprInfixBarBar2", mOp.EndRange)) }

| declExpr INFIX_BAR_OP declExpr
{ mkSynInfix (rhs parseState 2) $1 $2 $3 }

Expand All @@ -4690,6 +4710,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixBarOp", mOp.EndRange)) }

| declExpr INFIX_BAR_OP %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixBarOp2", mOp.EndRange)) }

| declExpr AMP_AMP declExpr
{ mkSynInfix (rhs parseState 2) $1 "&&" $3 }

Expand All @@ -4698,6 +4723,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "&&")
mkSynInfix mOp $1 "&&" (arbExpr ("declExprInfixAmpAmp", mOp.EndRange)) }

| declExpr AMP_AMP %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "&&")
mkSynInfix mOp $1 "&&" (arbExpr ("declExprInfixAmpAmp2", mOp.EndRange)) }

| declExpr INFIX_AMP_OP declExpr
{ mkSynInfix (rhs parseState 2) $1 $2 $3 }

Expand All @@ -4706,6 +4736,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixAmpOp", (rhs parseState 3).StartRange)) }

| declExpr INFIX_AMP_OP %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixAmpOp2", (rhs parseState 3).StartRange)) }

| declExpr EQUALS declExpr
{ mkSynInfix (rhs parseState 2) $1 "=" $3 }

Expand All @@ -4714,6 +4749,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "=")
mkSynInfix mOp $1 "=" (arbExpr ("declExprInfixEquals", mOp.EndRange)) }

| declExpr EQUALS %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "=")
mkSynInfix mOp $1 "=" (arbExpr ("declExprInfixEquals2", mOp.EndRange)) }

| declExpr INFIX_COMPARE_OP declExpr
{ mkSynInfix (rhs parseState 2) $1 $2 $3 }

Expand All @@ -4722,6 +4762,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfix", mOp.EndRange)) }

| declExpr INFIX_COMPARE_OP %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfix2", mOp.EndRange)) }

| declExpr DOLLAR declExpr
{ mkSynInfix (rhs parseState 2) $1 "$" $3 }

Expand All @@ -4730,6 +4775,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "$")
mkSynInfix mOp $1 "$" (arbExpr ("declExprInfixDollar", mOp.EndRange)) }

| declExpr DOLLAR %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "$")
mkSynInfix mOp $1 "$" (arbExpr ("declExprInfixDollar2", mOp.EndRange)) }

| declExpr LESS declExpr
{ mkSynInfix (rhs parseState 2) $1 "<" $3 }

Expand All @@ -4738,6 +4788,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "<")
mkSynInfix mOp $1 "<" (arbExpr ("declExprInfixLess", mOp.EndRange)) }

| declExpr LESS %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "<")
mkSynInfix mOp $1 "<" (arbExpr ("declExprInfixLess2", mOp.EndRange)) }

| declExpr GREATER declExpr
{ mkSynInfix (rhs parseState 2) $1 ">" $3 }

Expand All @@ -4746,6 +4801,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression ">")
mkSynInfix mOp $1 ">" (arbExpr ("declExprInfixGreater", mOp.EndRange)) }

| declExpr GREATER %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression ">")
mkSynInfix mOp $1 ">" (arbExpr ("declExprInfixGreater2", mOp.EndRange)) }

| declExpr INFIX_AT_HAT_OP declExpr
{ mkSynInfix (rhs parseState 2) $1 $2 $3 }

Expand All @@ -4762,6 +4822,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixPercent", mOp.EndRange)) }

| declExpr PERCENT_OP %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixPercent2", mOp.EndRange)) }

| declExpr COLON_COLON declExpr
{ let mOp = rhs parseState 2
let m = unionRanges $1.Range $3.Range
Expand All @@ -4777,6 +4842,14 @@ declExpr:
let tupExpr = SynExpr.Tuple(false, [$1; (arbExpr ("declExprInfixColonColon", mOp.EndRange))], [mOp], m)
SynExpr.App(ExprAtomicFlag.NonAtomic, true, identExpr, tupExpr, m) }

| declExpr COLON_COLON %prec prec_recover
{ let mOp = rhs parseState 2
let m = unionRanges $1.Range mOp
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "::")
let identExpr = mkSynOperator mOp "::"
let tupExpr = SynExpr.Tuple(false, [$1; (arbExpr ("declExprInfixColonColon2", mOp.EndRange))], [mOp], m)
SynExpr.App(ExprAtomicFlag.NonAtomic, true, identExpr, tupExpr, m) }

| declExpr PLUS_MINUS_OP declExpr
{ mkSynInfix (rhs parseState 2) $1 $2 $3 }

Expand All @@ -4785,6 +4858,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixPlusMinus", mOp.EndRange)) }

| declExpr PLUS_MINUS_OP %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixPlusMinus2", mOp.EndRange)) }

| declExpr MINUS declExpr
{ mkSynInfix (rhs parseState 2) $1 "-" $3 }

Expand All @@ -4793,6 +4871,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "-")
mkSynInfix mOp $1 "-" (arbExpr ("declExprInfixMinus", mOp.EndRange)) }

| declExpr MINUS %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "-")
mkSynInfix mOp $1 "-" (arbExpr ("declExprInfixMinus2", mOp.EndRange)) }

| declExpr STAR declExpr
{ mkSynInfix (rhs parseState 2) $1 "*" $3 }

Expand All @@ -4801,6 +4884,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "*")
mkSynInfix mOp $1 "*" (arbExpr ("declExprInfixStar", mOp.EndRange)) }

| declExpr STAR %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "*")
mkSynInfix mOp $1 "*" (arbExpr ("declExprInfixStar2", mOp.EndRange)) }

| declExpr INFIX_STAR_DIV_MOD_OP declExpr
{ mkSynInfix (rhs parseState 2) $1 $2 $3 }

Expand All @@ -4809,6 +4897,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixStarDivMod", mOp.EndRange)) }

| declExpr INFIX_STAR_DIV_MOD_OP %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixStarDivMod2", mOp.EndRange)) }

| declExpr INFIX_STAR_STAR_OP declExpr
{ mkSynInfix (rhs parseState 2) $1 $2 $3 }

Expand All @@ -4817,6 +4910,11 @@ declExpr:
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixStarStar", mOp.EndRange)) }

| declExpr INFIX_STAR_STAR_OP %prec prec_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixStarStar2", mOp.EndRange)) }

| declExpr DOT_DOT declExpr
{ let wholem = rhs2 parseState 1 3
let mOperator = rhs parseState 2
Expand Down Expand Up @@ -6935,7 +7033,7 @@ pathOp:
{ let ident, trivia = $1
SynLongIdent([ident], [], [Some trivia]) }

| ident DOT %prec prec_atompat_pathop_error
| ident DOT %prec prec_recover
{ let mDot = rhs parseState 2
reportParseErrorAt mDot.EndRange (FSComp.SR.parsIdentifierExpected())
SynLongIdent([$1], [mDot], [None]) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,27 +242,9 @@ module A
"""
|> typecheck
|> shouldFail
|> withResults [
{ Error = Error 10
Range = { StartLine = 5
StartColumn = 1
EndLine = 5
EndColumn = 3 }
Message =
"Incomplete structured construct at or before this point in expression" };
{ Error = Error 589
Range = { StartLine = 3
StartColumn = 13
EndLine = 3
EndColumn = 15 }
Message =
"Incomplete conditional. Expected 'if <expr> then <expr>' or 'if <expr> then <expr> else <expr>'." };
{ Error = Error 10
Range = { StartLine = 5
StartColumn = 31
EndLine = 5
EndColumn = 33 }
Message = "Unexpected infix operator in implementation file" }
|> withDiagnostics [
Error 589, Line 3, Col 13, Line 3, Col 15, "Incomplete conditional. Expected 'if <expr> then <expr>' or 'if <expr> then <expr> else <expr>'."
Error 10, Line 5, Col 31, Line 5, Col 33, "Unexpected infix operator in implementation file"
] |> ignore


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ImplFile
[Some (OriginalNotation "=")]), None,
(3,4--3,5)), Ident a, (3,2--3,5)),
ArbitraryAfterError
("declExprInfixEquals", (3,5--3,5)), (3,2--3,5));
("declExprInfixEquals2", (3,5--3,5)), (3,2--3,5));
App
(NonAtomic, false,
App
Expand All @@ -41,5 +41,4 @@ ImplFile
WarnDirectives = []
CodeComments = [] }, set []))

(3,5)-(3,6) parse error Unexpected symbol ',' in expression
(3,4)-(3,5) parse error Unexpected token '=' or incomplete expression
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ ImplFile
[Some (OriginalNotation "=")]), None,
(3,11--3,12)), Ident b, (3,9--3,12)),
ArbitraryAfterError
("declExprInfixEquals", (3,12--3,12)), (3,9--3,12));
("declExprInfixEquals2", (3,12--3,12)), (3,9--3,12));
App
(NonAtomic, false,
App
Expand All @@ -52,5 +52,4 @@ ImplFile
WarnDirectives = []
CodeComments = [] }, set []))

(3,12)-(3,13) parse error Unexpected symbol ',' in expression
(3,11)-(3,12) parse error Unexpected token '=' or incomplete expression
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ImplFile
([op_Addition], [], [Some (OriginalNotation "+")]),
None, (3,2--3,3)), Ident a, (3,0--3,3)),
ArbitraryAfterError
("declExprInfixPlusMinus", (3,3--3,3)), (3,0--3,3)),
("declExprInfixPlusMinus2", (3,3--3,3)), (3,0--3,3)),
(3,0--4,2)), Const (Unit, (4,3--4,5)), (3,0--4,5)),
(3,0--4,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
Expand All @@ -33,5 +33,4 @@ ImplFile
WarnDirectives = []
CodeComments = [] }, set []))

(4,0)-(4,2) parse error Unexpected infix operator in expression
(3,2)-(3,3) parse error Unexpected token '+' or incomplete expression
3 changes: 1 addition & 2 deletions tests/service/data/SyntaxTree/Expression/If 03.fs.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ImplFile
[Expr
(IfThenElse
(Const (Bool true, (3,3--3,7)),
ArbitraryAfterError ("if1", (3,7--3,7)), None, Yes (3,0--3,7),
ArbitraryAfterError ("if4", (3,7--3,7)), None, Yes (3,0--3,7),
true, (3,0--3,7), { IfKeyword = (3,0--3,2)
IsElif = false
ThenKeyword = (3,7--3,7)
Expand All @@ -19,5 +19,4 @@ ImplFile
WarnDirectives = []
CodeComments = [] }, set []))

(3,8)-(5,0) parse error Incomplete structured construct at or before this point in expression
(3,0)-(3,2) parse error Incomplete conditional. Expected 'if <expr> then <expr>' or 'if <expr> then <expr> else <expr>'.
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Expression/If 11.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

do
if true &&

()
42 changes: 42 additions & 0 deletions tests/service/data/SyntaxTree/Expression/If 11.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/If 11.fs", false, QualifiedNameOfFile Module, [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(Do
(Sequential
(SuppressNeither, true,
IfThenElse
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_BooleanAnd], [],
[Some (OriginalNotation "&&")]), None,
(4,12--4,14)), Const (Bool true, (4,7--4,11)),
(4,7--4,14)),
ArbitraryAfterError
("declExprInfixAmpAmp2", (4,14--4,14)), (4,7--4,14)),
ArbitraryAfterError ("if4", (4,14--4,14)), None,
Yes (4,4--4,14), true, (4,4--4,14),
{ IfKeyword = (4,4--4,6)
IsElif = false
ThenKeyword = (4,14--4,14)
ElseKeyword = None
IfToThenRange = (4,4--4,14) }),
Const (Unit, (6,4--6,6)), (4,4--6,6),
{ SeparatorRange = None }), (3,0--6,6)), (3,0--6,6))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,6), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
WarnDirectives = []
CodeComments = [] }, set []))

(6,4)-(6,5) parse error Unexpected syntax or possible incorrect indentation: this token is offside of context started at position (4:5). Try indenting this further.
To continue using non-conforming indentation, pass the '--strict-indentation-' flag to the compiler, or set the language version to F# 7.
(4,12)-(4,14) parse error Unexpected token '&&' or incomplete expression
(4,4)-(4,6) parse error Incomplete conditional. Expected 'if <expr> then <expr>' or 'if <expr> then <expr> else <expr>'.
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Expression/If 12.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

do
if true &&

()
Loading
Loading