Skip to content

Commit

Permalink
Parser: more binary expressions recovery (#15255)
Browse files Browse the repository at this point in the history
  • Loading branch information
auduchinok authored May 25, 2023
1 parent 0d81a05 commit 3cdf2d2
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 91 deletions.
193 changes: 105 additions & 88 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -4024,151 +4024,168 @@ declExpr:
| declExpr JOIN_IN declExpr
{ SynExpr.JoinIn($1, rhs parseState 2, $3, unionRanges $1.Range $3.Range) }

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

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

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

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

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

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

| declExpr OR ends_coming_soon_or_recover
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression "or")
mkSynInfix (rhs parseState 2) $1 "or" (arbExpr ("declExprInfixOr", (rhs parseState 3).StartRange)) }

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

| declExpr AMP ends_coming_soon_or_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "&")
mkSynInfix mOp $1 "&" (arbExpr ("declExprInfixAmp", mOp.EndRange)) }

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

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

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

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

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

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

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

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

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

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

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

| declExpr LESS recover
{ if not $3 then reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("<"))
exprFromParseError (mkSynInfix (rhs parseState 2) $1 "<" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }
| declExpr LESS ends_coming_soon_or_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression "<")
mkSynInfix mOp $1 "<" (arbExpr ("declExprInfixLess", mOp.EndRange)) }

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

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

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

| declExpr INFIX_AT_HAT_OP ends_coming_soon_or_recover %prec infix_at_hat_op_binary
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfix", mOp.EndRange)) }

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

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

| declExpr COLON_COLON declExpr
{ let tupExpr = SynExpr.Tuple(false, [$1;$3], [rhs parseState 2], unionRanges $1.Range $3.Range)
let identExpr = mkSynOperator (rhs parseState 2) "::"
SynExpr.App(ExprAtomicFlag.NonAtomic, true, identExpr, tupExpr, unionRanges $1.Range $3.Range) }
{ let mOp = rhs parseState 2
let m = unionRanges $1.Range $3.Range
let tupExpr = SynExpr.Tuple(false, [$1; $3], [mOp], m)
let identExpr = mkSynOperator mOp "::"
SynExpr.App(ExprAtomicFlag.NonAtomic, true, identExpr, tupExpr, m) }

| declExpr COLON_COLON ends_coming_soon_or_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 ("declExprInfixColonColon", mOp.EndRange))], [mOp], m)
SynExpr.App(ExprAtomicFlag.NonAtomic, true, identExpr, tupExpr, m) }

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

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

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

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

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

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

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

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

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

| declExpr JOIN_IN OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("in"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "@in" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr BAR_BAR OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("||"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "||" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr INFIX_BAR_OP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression($2))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 $2 (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr OR OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("or"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "or" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr AMP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("&"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "&" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr AMP_AMP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("&&"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "&&" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr INFIX_AMP_OP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression($2))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 $2 (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr EQUALS ends_coming_soon_or_recover
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("="))
let mEquals = rhs parseState 2
mkSynInfix mEquals $1 "=" (arbExpr ("declExprInfixEquals", mEquals.EndRange)) }

| declExpr INFIX_COMPARE_OP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression($2))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 $2 (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr DOLLAR OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("$"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "$" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr LESS OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("<"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "<" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr GREATER OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression(">"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 ">" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr INFIX_AT_HAT_OP OBLOCKEND_COMING_SOON %prec infix_at_hat_op_binary
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression($2))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 $2 (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr PERCENT_OP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression($2))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 $2 (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr COLON_COLON OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("::"))
let identExpr = mkSynOperator (rhs parseState 2) "::"
let tupExpr = SynExpr.Tuple(false, [$1;(arbExpr ("declExprInfix", (rhs parseState 3).StartRange))], [rhs parseState 2], unionRanges $1.Range (rhs parseState 3).StartRange)
SynExpr.App(ExprAtomicFlag.NonAtomic, true, identExpr, tupExpr, unionRanges $1.Range (rhs parseState 3).StartRange) }

| declExpr PLUS_MINUS_OP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression($2))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 $2 (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr MINUS OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("-"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "-" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr STAR OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression("*"))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 "*" (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr INFIX_STAR_DIV_MOD_OP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression($2))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 $2 (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }

| declExpr INFIX_STAR_STAR_OP OBLOCKEND_COMING_SOON
{ reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnfinishedExpression($2))
exprFromParseError(mkSynInfix (rhs parseState 2) $1 $2 (arbExpr ("declExprInfix", (rhs parseState 3).StartRange))) }
| declExpr INFIX_STAR_STAR_OP ends_coming_soon_or_recover
{ let mOp = rhs parseState 2
reportParseErrorAt mOp (FSComp.SR.parsUnfinishedExpression $2)
mkSynInfix mOp $1 $2 (arbExpr ("declExprInfixStarStar", mOp.EndRange)) }

| declExpr DOT_DOT declExpr
{ let wholem = rhs2 parseState 1 3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// Since OCaml-style comments are now gone, this is going to be a negative test
//<Expects id="FS0010" span="(12,47)" status="error">Unexpected string literal in binding\. Expected incomplete structured construct at or before this point or other token\.$</Expects>
//<Expects status="error" span="(10,1)" id="FS3118">Incomplete value or function definition\. If this is in an expression, the body of the expression must be indented to the same column as the 'let' keyword\.$</Expects>
//<Expects id="FS0010" span="(23,1)" status="error">Incomplete structured construct at or before this point in implementation file$</Expects>

//<Expects id="FS3156" span="(14,19)" status="error">Unexpected token '\*' or incomplete expression</Expects>
//<Expects id="FS0010" span="(14,20)" status="error">Unexpected symbol '\)' in implementation file</Expects>

let y7a = (** This is a comment with "(***)" *) 1 (**) + 2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// #Regression #Conformance #UnitsOfMeasure
// Regression test for FSHARP1.0:2662
// Make sure we can use ( and ) in Units of Measure
//<Expects status="error" span="(10,26-10,27)" id="FS0010">Unexpected symbol '\)' in expression$</Expects>
//<Expects status="error" span="(11,26-11,27)" id="FS0010">Unexpected symbol '\)' in binding\. Expected incomplete structured construct at or before this point or other token\.</Expects>
//<Expects status="error" span="(11,24-11,25)" id="FS3156">Unexpected token '/' or incomplete expression</Expects>

[<Measure>] type Kg
[<Measure>] type m
Expand Down
3 changes: 3 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Binary - Plus 01.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

a + b
21 changes: 21 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Binary - Plus 01.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Binary - Plus 01.fs", false, QualifiedNameOfFile Module,
[], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Addition], [], [Some (OriginalNotation "+")]),
None, (3,2--3,3)), Ident a, (3,0--3,3)), Ident b,
(3,0--3,5)), (3,0--3,5))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--3,5), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))
3 changes: 3 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Binary - Plus 02.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

a +
25 changes: 25 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Binary - Plus 02.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Binary - Plus 02.fs", false, QualifiedNameOfFile Module,
[], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([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)), (3,0--3,3))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--3,3), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(4,0)-(4,0) parse warning Possible incorrect indentation: this token is offside of context started at position (3:1). Try indenting this token further or using standard formatting conventions.
(3,2)-(3,3) parse error Unexpected token '+' or incomplete expression
3 changes: 3 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Binary - Plus 03.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

M(a + )
29 changes: 29 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Binary - Plus 03.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
ImplFile
(ParsedImplFileInput
("/root/Expression/Binary - Plus 03.fs", false, QualifiedNameOfFile Module,
[], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Expr
(App
(Atomic, false, Ident M,
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Addition], [], [Some (OriginalNotation "+")]),
None, (3,4--3,5)), Ident a, (3,2--3,5)),
ArbitraryAfterError
("declExprInfixPlusMinus", (3,5--3,5)), (3,2--3,5)),
(3,1--3,2), Some (3,6--3,7), (3,1--3,7)), (3,0--3,7)),
(3,0--3,7))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--3,7), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(3,4)-(3,5) parse error Unexpected token '+' or incomplete expression
4 changes: 4 additions & 0 deletions tests/service/data/SyntaxTree/Expression/Binary - Plus 04.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

a +
|> ()
Loading

0 comments on commit 3cdf2d2

Please sign in to comment.