diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fs b/src/Compiler/SyntaxTree/SyntaxTrivia.fs index 999f93d29a5b..e2b16e3ff8c0 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fs +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fs @@ -79,7 +79,7 @@ type SynExprLambdaTrivia = type SynExprDotLambdaTrivia = { UnderscoreRange: range - DotRange: range + DotRange: range option } [] diff --git a/src/Compiler/SyntaxTree/SyntaxTrivia.fsi b/src/Compiler/SyntaxTree/SyntaxTrivia.fsi index d30816e697b6..2fd81c6d2a43 100644 --- a/src/Compiler/SyntaxTree/SyntaxTrivia.fsi +++ b/src/Compiler/SyntaxTree/SyntaxTrivia.fsi @@ -123,7 +123,7 @@ type SynExprLambdaTrivia = [] type SynExprDotLambdaTrivia = { UnderscoreRange: range - DotRange: range } + DotRange: range option } /// Represents additional information for SynExpr.LetOrUse [] diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy index 38d01ec99d74..c6048f6f7f06 100644 --- a/src/Compiler/pars.fsy +++ b/src/Compiler/pars.fsy @@ -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 @@ -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 diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index aabefaf4b35a..b76f6115c7e8 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -2,6 +2,7 @@ namespace FSharp.Test +open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Interactive.Shell open FSharp.Compiler.IO open FSharp.Compiler.Diagnostics @@ -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 diff --git a/tests/service/Common.fs b/tests/service/Common.fs index 4f24b831d2cf..5ea61e33668d 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -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 @@ -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 diff --git a/tests/service/ParserTests.fs b/tests/service/ParserTests.fs index cb5318013142..eb20e1942e60 100644 --- a/tests/service/ParserTests.fs +++ b/tests/service/ParserTests.fs @@ -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 @@ -368,3 +369,65 @@ match () with match exprs with | SynExpr.Match(_, _, [_; _], _, _) -> () | _ -> failwith "Unexpected tree" + + +[] +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"|] + +[] +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."|] + +[] +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"|] + diff --git a/tests/service/XmlDocTests.fs b/tests/service/XmlDocTests.fs index bdb4fd814442..7a2aa0cabfa7 100644 --- a/tests/service/XmlDocTests.fs +++ b/tests/service/XmlDocTests.fs @@ -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 - [] let ``xml-doc eof``(): unit = checkSignatureAndImplementation """