Skip to content

Commit

Permalink
DotLambda: add parser recovery
Browse files Browse the repository at this point in the history
  • Loading branch information
DedSec256 committed Nov 7, 2023
1 parent 24ef671 commit ae4676d
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/Compiler/SyntaxTree/SyntaxTrivia.fs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type SynExprLambdaTrivia =
type SynExprDotLambdaTrivia =
{
UnderscoreRange: range
DotRange: range
DotRange: range option
}

[<NoEquality; NoComparison>]
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/SyntaxTree/SyntaxTrivia.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ type SynExprLambdaTrivia =
[<NoEquality; NoComparison>]
type SynExprDotLambdaTrivia =
{ UnderscoreRange: range
DotRange: range }
DotRange: range option }

/// Represents additional information for SynExpr.LetOrUse
[<NoEquality; NoComparison>]
Expand Down
27 changes: 23 additions & 4 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -4882,7 +4882,7 @@ atomicExpr:
let mDot = rhs parseState 2
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AccessorFunctionShorthand (unionRanges mUnderscore mDot )
let expr, hpa = $3
let trivia: SynExprDotLambdaTrivia = { UnderscoreRange = mUnderscore ; DotRange = mDot }
let trivia: SynExprDotLambdaTrivia = { UnderscoreRange = mUnderscore ; DotRange = Some mDot }
SynExpr.DotLambda(expr, unionRanges mUnderscore expr.Range, trivia), false }

| UNDERSCORE DOT appExpr recover %prec dot_lambda
Expand All @@ -4891,9 +4891,28 @@ atomicExpr:
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AccessorFunctionShorthand (unionRanges mUnderscore mDot )
reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnderScoreDotLambdaNonAtomic())
let expr = $3
let trivia: SynExprDotLambdaTrivia = { UnderscoreRange = mUnderscore ; DotRange = mDot }
SynExpr.DotLambda(expr, unionRanges mUnderscore expr.Range, trivia), false }

let trivia: SynExprDotLambdaTrivia = { UnderscoreRange = mUnderscore ; DotRange = Some mDot }
SynExpr.DotLambda(expr, unionRanges mUnderscore expr.Range, trivia), false }

| UNDERSCORE DOT recover %prec dot_lambda
{ let mUnderscore = rhs parseState 1
let mDot = rhs parseState 2
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AccessorFunctionShorthand (unionRanges mUnderscore mDot)
let expr = arbExpr ("dotLambda1", mDot.EndRange)
let trivia: SynExprDotLambdaTrivia = { UnderscoreRange = mUnderscore ; DotRange = Some mDot }
SynExpr.DotLambda(expr, unionRanges mUnderscore expr.Range, trivia), false }

| UNDERSCORE recover %prec dot_lambda
{ let mUnderscore = rhs parseState 1
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AccessorFunctionShorthand mUnderscore
let mExpr = mUnderscore.EndRange

if not $2 then reportParseErrorAt mExpr (FSComp.SR.parsUnexpectedEndOfFileExpression())

let expr = arbExpr ("dotLambda2", mExpr)
let trivia: SynExprDotLambdaTrivia = { UnderscoreRange = mUnderscore ; DotRange = None }
SynExpr.DotLambda(expr, mUnderscore, trivia), false }

| atomicExpr HIGH_PRECEDENCE_BRACK_APP atomicExpr
{ let arg1, _ = $1
let arg2, hpa = $3
Expand Down
8 changes: 8 additions & 0 deletions tests/FSharp.Test.Utilities/Compiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace FSharp.Test

open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Interactive.Shell
open FSharp.Compiler.IO
open FSharp.Compiler.Diagnostics
Expand Down Expand Up @@ -148,6 +149,13 @@ module rec Compiler =
| FSharpDiagnosticSeverity.Warning -> Warning errorNumber
| FSharpDiagnosticSeverity.Error -> Error errorNumber

let getParsingErrors (parseResults: FSharpParseFileResults) =
parseResults.Diagnostics |> Array.map (fun x ->
let range = x.Range
let error = mapDiagnosticSeverity x.Severity x.ErrorNumber
error, Line range.StartLine, Col range.StartColumn, Line range.EndLine, Col range.EndColumn, x.Message
)

type Line = Line of int
type Col = Col of int

Expand Down
3 changes: 3 additions & 0 deletions tests/service/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ open FSharp.Compiler.Diagnostics
open FSharp.Compiler.Symbols
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
open FSharp.Test.Compiler
open TestFramework
open FsUnit
open NUnit.Framework
Expand Down Expand Up @@ -364,6 +365,8 @@ let getParseAndCheckResults50 (source: string) =
let getParseAndCheckResults70 (source: string) =
parseAndCheckScript70("Test.fsx", source)

let checkParsingErrors expected (parseResults: FSharpParseFileResults) =
getParsingErrors parseResults |> shouldEqual expected

let inline dumpDiagnostics (results: FSharpCheckFileResults) =
results.Diagnostics
Expand Down
63 changes: 63 additions & 0 deletions tests/service/ParserTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
open FSharp.Compiler.Service.Tests.Common
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
open FSharp.Test.Compiler
open FsUnit
open NUnit.Framework

Expand Down Expand Up @@ -368,3 +369,65 @@ match () with
match exprs with
| SynExpr.Match(_, _, [_; _], _, _) -> ()
| _ -> failwith "Unexpected tree"


[<Test>]
let ``Dot lambda - Recovery _.`` () =
let parseResults, _ = getParseAndCheckResults """
(_.)
"""
let expr = getSingleExprInModule parseResults.ParseTree
match expr with
| SynExpr.Paren(expr =
SynExpr.DotLambda(
expr = SynExpr.ArbitraryAfterError("dotLambda1", exprRange)
range = lambdaRange
trivia = { UnderscoreRange = underscoreRange; DotRange = Some dotRange }
)) ->
assertRange (2, 3) (2, 3) exprRange
assertRange (2, 1) (2, 3) lambdaRange
assertRange (2, 2) (2, 3) dotRange
| tree -> failwith $"Unexpected tree: {tree}"

parseResults
|> checkParsingErrors [|Error 10, Line 2, Col 3, Line 2, Col 4, "Unexpected symbol ')' in expression"|]

[<Test>]
let ``Dot lambda - Recovery _`` () =
let parseResults, _ = getParseAndCheckResults """
(_)
"""
let expr = getSingleExprInModule parseResults.ParseTree
match expr with
| SynExpr.Paren(expr =
SynExpr.DotLambda(
expr = SynExpr.ArbitraryAfterError("dotLambda2", exprRange)
range = lambdaRange
trivia = { UnderscoreRange = underscoreRange; DotRange = None }
)) ->
assertRange (2, 2) (2, 2) exprRange
assertRange (2, 1) (2, 2) lambdaRange
| tree -> failwith $"Unexpected tree: {tree}"

parseResults
|> checkParsingErrors [|Error 10, Line 2, Col 2, Line 2, Col 3, "Unexpected symbol ')' in expression. Expected '.' or other token."|]

[<Test>]
let ``Dot lambda - Recovery _ - eof`` () =
let parseResults, _ = getParseAndCheckResults """
1 |> _"""
let exprs = getSingleExprInModule parseResults.ParseTree
match exprs with
| SynExpr.App(argExpr =
SynExpr.DotLambda(
expr = SynExpr.ArbitraryAfterError("dotLambda2", exprRange)
range = lambdaRange
trivia = { UnderscoreRange = underscoreRange; DotRange = None }
)) ->
assertRange (2, 6) (2, 6) exprRange
assertRange (2, 5) (2, 6) lambdaRange
| tree -> failwith $"Unexpected tree: {tree}"

parseResults
|> checkParsingErrors [|Error 3116, Line 2, Col 6, Line 2, Col 6, "Unexpected end of input in expression"|]

7 changes: 0 additions & 7 deletions tests/service/XmlDocTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,6 @@ let checkSignatureAndImplementation code checkResultsAction parseResultsAction =
checkCode getParseAndCheckResults
checkCode getParseAndCheckResultsOfSignatureFile

let checkParsingErrors expected (parseResults: FSharpParseFileResults) =
parseResults.Diagnostics |> Array.map (fun x ->
let range = x.Range
let error = mapDiagnosticSeverity x.Severity x.ErrorNumber
error, Line range.StartLine, Col range.StartColumn, Line range.EndLine, Col range.EndColumn, x.Message)
|> shouldEqual expected

[<Test>]
let ``xml-doc eof``(): unit =
checkSignatureAndImplementation """
Expand Down

0 comments on commit ae4676d

Please sign in to comment.