Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 0855b78

Browse files
auduchinoknosami
authored andcommitted
Add parser recovery for unfinished interface implementation (dotnet#10416)
* Add parser recovery for unfinished interface implementation * Report indentation problem via parser * Update public area
1 parent 4cb5aa1 commit 0855b78

File tree

9 files changed

+79
-16
lines changed

9 files changed

+79
-16
lines changed

src/fsharp/LexFilter.fs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ let outputPos os (p: Position) = Printf.fprintf os "(%d:%d)" p.OriginalLine p.Co
2222
/// Used for warning strings, which should display columns as 1-based and display
2323
/// the lines after taking '# line' directives into account (i.e. do not use
2424
/// p.OriginalLine)
25-
let warningStringOfPos (p: Position) = sprintf "(%d:%d)" p.Line (p.Column + 1)
25+
let warningStringOfPosition (p: Position) = warningStringOfCoords p.Line p.Column
2626

2727
type Context =
2828
// Position is position of keyword.
@@ -651,7 +651,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu
651651
initialLookaheadTokenTup
652652

653653
let warn (s: TokenTup) msg =
654-
warning(Lexhelp.IndentationProblem(msg, mkSynRange (startPosOfTokenTup s) s.LexbufState.EndPos))
654+
warning(IndentationProblem(msg, mkSynRange (startPosOfTokenTup s) s.LexbufState.EndPos))
655655

656656
// 'query { join x in ys ... }'
657657
// 'query { ...
@@ -865,9 +865,9 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu
865865
warn tokenTup
866866
(if debug then
867867
sprintf "possible incorrect indentation: this token is offside of context at position %s, newCtxt = %A, stack = %A, newCtxtPos = %s, c1 = %d, c2 = %d"
868-
(warningStringOfPos p1.Position) newCtxt offsideStack (stringOfPos (newCtxt.StartPos)) p1.Column c2
868+
(warningStringOfPosition p1.Position) newCtxt offsideStack (stringOfPos (newCtxt.StartPos)) p1.Column c2
869869
else
870-
FSComp.SR.lexfltTokenIsOffsideOfContextStartedEarlier(warningStringOfPos p1.Position))
870+
FSComp.SR.lexfltTokenIsOffsideOfContextStartedEarlier(warningStringOfPosition p1.Position))
871871
let newOffsideStack = newCtxt :: offsideStack
872872
if debug then dprintf "--> pushing, stack = %A\n" newOffsideStack
873873
offsideStack <- newOffsideStack
@@ -1749,7 +1749,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu
17491749
let cond1 = tokenStartCol + (if leadingBar then 0 else 2) < offsidePos.Column
17501750
let cond2 = tokenStartCol + (if leadingBar then 1 else 2) < offsidePos.Column
17511751
if (cond1 <> cond2) then
1752-
errorR(Lexhelp.IndentationProblem(FSComp.SR.lexfltSeparatorTokensOfPatternMatchMisaligned(), mkSynRange (startPosOfTokenTup tokenTup) tokenTup.LexbufState.EndPos))
1752+
errorR(IndentationProblem(FSComp.SR.lexfltSeparatorTokensOfPatternMatchMisaligned(), mkSynRange (startPosOfTokenTup tokenTup) tokenTup.LexbufState.EndPos))
17531753
cond1
17541754
| END -> tokenStartCol + (if leadingBar then -1 else 1) < offsidePos.Column
17551755
| _ -> tokenStartCol + (if leadingBar then -1 else 1) < offsidePos.Column)) ->
@@ -2066,6 +2066,11 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu
20662066
let offsidePos = tokenStartPos
20672067
pushCtxt tokenTup (CtxtWithAsLet offsidePos)
20682068
returnToken tokenLexbufState OWITH
2069+
2070+
// Recovery for `interface ... with` member without further indented member implementations
2071+
elif lookaheadTokenStartPos.Column <= limCtxt.StartCol && (match limCtxt with CtxtInterfaceHead _ -> true | _ -> false) then
2072+
returnToken tokenLexbufState token
2073+
20692074
else
20702075
// In these situations
20712076
// interface I with

src/fsharp/ParseHelpers.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ open Internal.Utilities.Text.Parsing
2323
[<NoEquality; NoComparison>]
2424
exception SyntaxError of obj (* ParseErrorContext<_> *) * range: range
2525

26+
exception IndentationProblem of string * range
27+
28+
let warningStringOfCoords line column =
29+
sprintf "(%d:%d)" line (column + 1)
30+
31+
let warningStringOfPos (p: pos) =
32+
warningStringOfCoords p.Line p.Column
33+
2634
//------------------------------------------------------------------------
2735
// Parsing: getting positions from the lexer
2836
//------------------------------------------------------------------------

src/fsharp/lexhelp.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@ let escape c =
230230
//-----------------------------------------------------------------------
231231

232232
exception ReservedKeyword of string * range
233-
exception IndentationProblem of string * range
234233

235234
module Keywords =
236235
type private compatibilityMode =

src/fsharp/lexhelp.fsi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ val escape: char -> char
9393

9494
exception ReservedKeyword of string * Range.range
9595

96-
exception IndentationProblem of string * Range.range
97-
9896
module Keywords =
9997

10098
val KeywordOrIdentifierToken: LexArgs -> UnicodeLexing.Lexbuf -> string -> token

src/fsharp/pars.fsy

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,9 +1649,13 @@ classDefnMembers:
16491649

16501650

16511651
/* The members of an object type definition or type augmentation */
1652-
classDefnMembersAtLeastOne:
1653-
| classDefnMember opt_seps classDefnMembers
1654-
{ $1 @ $3 }
1652+
classDefnMembersAtLeastOne:
1653+
| classDefnMember opt_seps classDefnMembers
1654+
{ match $1, $3 with
1655+
| [ SynMemberDefn.Interface (_, Some [], m) ], nextMember :: _ ->
1656+
warning(IndentationProblem(FSComp.SR.lexfltTokenIsOffsideOfContextStartedEarlier(warningStringOfPos m.Start), nextMember.Range))
1657+
| _ -> ()
1658+
$1 @ $3 }
16551659

16561660

16571661
/* The "with get, set" part of a member definition */
@@ -1904,11 +1908,12 @@ classDefnMember:
19041908
| opt_attributes opt_declVisibility interfaceMember appType opt_interfaceImplDefn
19051909
{ if not (isNil $1) then errorR(Error(FSComp.SR.parsAttributesAreNotPermittedOnInterfaceImplementations(), rhs parseState 1))
19061910
if Option.isSome $2 then errorR(Error(FSComp.SR.parsInterfacesHaveSameVisibilityAsEnclosingType(), rhs parseState 3))
1907-
let mWhole =
1911+
let members = Option.map fst $5
1912+
let mWhole =
19081913
match $5 with
19091914
| None -> rhs2 parseState 1 4
1910-
| Some(mems) -> (rhs2 parseState 1 4, mems) ||> unionRangeWithListBy (fun (mem:SynMemberDefn) -> mem.Range)
1911-
[ SynMemberDefn.Interface ($4, $5, mWhole) ] }
1915+
| Some (_, m) -> unionRanges (rhs2 parseState 1 4) m
1916+
[ SynMemberDefn.Interface ($4, members, mWhole) ] }
19121917

19131918
| opt_attributes opt_declVisibility abstractMemberFlags opt_inline nameop opt_explicitValTyparDecls COLON topTypeWithTypeConstraints classMemberSpfnGetSet opt_ODECLEND
19141919
{ let ty, arity = $8
@@ -2042,9 +2047,14 @@ opt_declVisibility:
20422047
{ None }
20432048

20442049

2045-
opt_interfaceImplDefn:
2050+
opt_interfaceImplDefn:
20462051
| WITH objectImplementationBlock declEnd
2047-
{ Some($2) }
2052+
{ let members = $2
2053+
let m = (rhs parseState 1, members) ||> unionRangeWithListBy (fun (mem:SynMemberDefn) -> mem.Range)
2054+
Some (members, m) }
2055+
2056+
| WITH
2057+
{ Some ([], rhs parseState 1) }
20482058

20492059
| /* EMPTY */
20502060
{ None }

tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
<Compile Include="..\service\ScriptOptionsTests.fs">
6868
<Link>ScriptOptionsTests.fs</Link>
6969
</Compile>
70+
<Compile Include="..\service\ParserTests.fs" >
71+
<Link>ParserTests.fs</Link>
72+
</Compile>
7073
<Compile Include="..\service\Program.fs">
7174
<Link>Program.fs</Link>
7275
</Compile>

tests/FSharp.Compiler.Service.Tests/SurfaceArea.netstandard.fs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20020,6 +20020,20 @@ FSharp.Compiler.ParseHelpers: FSharp.Compiler.ParseHelpers+LexerStringStyle
2002020020
FSharp.Compiler.ParseHelpers: FSharp.Compiler.ParseHelpers+SyntaxError
2002120021
FSharp.Compiler.ParseHelpers: ILInstr[] ParseAssemblyCodeInstructions(System.String, range)
2002220022
FSharp.Compiler.ParseHelpers: ILType ParseAssemblyCodeType(System.String, range)
20023+
FSharp.Compiler.ParseHelpers+IndentationProblem: Boolean Equals(System.Exception)
20024+
FSharp.Compiler.ParseHelpers+IndentationProblem: Boolean Equals(System.Object)
20025+
FSharp.Compiler.ParseHelpers+IndentationProblem: Boolean Equals(System.Object, System.Collections.IEqualityComparer)
20026+
FSharp.Compiler.ParseHelpers+IndentationProblem: Int32 GetHashCode()
20027+
FSharp.Compiler.ParseHelpers+IndentationProblem: Int32 GetHashCode(System.Collections.IEqualityComparer)
20028+
FSharp.Compiler.ParseHelpers+IndentationProblem: System.String Data0
20029+
FSharp.Compiler.ParseHelpers+IndentationProblem: System.String get_Data0()
20030+
FSharp.Compiler.ParseHelpers+IndentationProblem: Void .ctor()
20031+
FSharp.Compiler.ParseHelpers+IndentationProblem: Void .ctor(System.String, range)
20032+
FSharp.Compiler.ParseHelpers+IndentationProblem: range Data1
20033+
FSharp.Compiler.ParseHelpers+IndentationProblem: range get_Data1()
20034+
FSharp.Compiler.ParseHelpers: FSharp.Compiler.ParseHelpers+IndentationProblem
20035+
FSharp.Compiler.ParseHelpers: System.String warningStringOfCoords(Int32, Int32)
20036+
FSharp.Compiler.ParseHelpers: System.String warningStringOfPos(pos)
2002320037
FSharp.Compiler.PartialLongName: Boolean Equals(FSharp.Compiler.PartialLongName)
2002420038
FSharp.Compiler.PartialLongName: Boolean Equals(System.Object)
2002520039
FSharp.Compiler.PartialLongName: Boolean Equals(System.Object, System.Collections.IEqualityComparer)

tests/service/Common.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,14 @@ let rec allSymbolsInEntities compGen (entities: IList<FSharpEntity>) =
337337
yield (x :> FSharpSymbol)
338338
yield! allSymbolsInEntities compGen e.NestedEntities ]
339339

340+
341+
let getParseResults (source: string) =
342+
parseSourceCode("/home/user/Test.fsx", source)
343+
340344
let getParseAndCheckResults (source: string) =
341345
parseAndCheckScript("/home/user/Test.fsx", source)
342346

347+
343348
let inline dumpErrors results =
344349
(^TResults: (member Errors: FSharpErrorInfo[]) results)
345350
|> Array.map (fun e ->

tests/service/ParserTests.fs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Tests.Parser
2+
3+
open FSharp.Compiler.Service.Tests.Common
4+
open FSharp.Compiler.SyntaxTree
5+
open NUnit.Framework
6+
7+
module Recovery =
8+
[<Test>]
9+
let ``Unfinished interface member`` () =
10+
let parseResults = getParseResults """
11+
type T =
12+
interface I with
13+
member x.P2 = ()
14+
15+
let x = ()
16+
"""
17+
let (SynModuleOrNamespace (decls = decls)) = getSingleModuleLikeDecl parseResults
18+
match decls with
19+
| [ SynModuleDecl.Types ([ TypeDefn (typeRepr = SynTypeDefnRepr.ObjectModel (members = [ _; _ ])) ], _)
20+
SynModuleDecl.Let _ ] -> ()
21+
| _ -> failwith "Unexpected tree"

0 commit comments

Comments
 (0)