Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parser: recover on unfinished interface member #15943

Merged
merged 6 commits into from
Sep 18, 2023
Merged
Changes from 1 commit
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
Next Next commit
Parser: recover on unfinished interface member
  • Loading branch information
auduchinok committed Sep 7, 2023
commit f6d61c1df5395c1ef03c260496bc6a472b7355ea
63 changes: 42 additions & 21 deletions src/Compiler/SyntaxTree/LexFilter.fs
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ type Context =
| CtxtElse of Position
| CtxtDo of Position
| CtxtInterfaceHead of Position
| CtxtTypeDefns of Position // 'type <here> =', not removed when we find the "="
| CtxtTypeDefns of Position * equalsEndPos: Position option // 'type <here> =', not removed when we find the "="

| CtxtNamespaceHead of Position * token
| CtxtModuleHead of Position * token * LexingModuleAttributes * isNested: bool
@@ -69,7 +69,7 @@ type Context =
member c.StartPos =
match c with
| CtxtNamespaceHead (p, _) | CtxtModuleHead (p, _, _, _) | CtxtException p | CtxtModuleBody (p, _) | CtxtNamespaceBody p
| CtxtLetDecl (_, p) | CtxtDo p | CtxtInterfaceHead p | CtxtTypeDefns p | CtxtParen (_, p) | CtxtMemberHead p | CtxtMemberBody p
| CtxtLetDecl (_, p) | CtxtDo p | CtxtInterfaceHead p | CtxtTypeDefns(p, _) | CtxtParen (_, p) | CtxtMemberHead p | CtxtMemberBody p
| CtxtWithAsLet p
| CtxtWithAsAugment p
| CtxtMatchClauses (_, p) | CtxtIf p | CtxtMatch p | CtxtFor p | CtxtWhile p | CtxtWhen p | CtxtFunction p | CtxtFun p | CtxtTry p | CtxtThen p | CtxtElse p | CtxtVanilla (p, _)
@@ -1679,7 +1679,7 @@ type LexFilterImpl (
// | B
//
// <TOKEN> <-- close the type context sequence block here *)
| _, CtxtTypeDefns posType :: _ when offsidePos.Column = posType.Column && not (isTypeSeqBlockElementContinuator token) -> -1
| _, CtxtTypeDefns(posType, _) :: _ when offsidePos.Column = posType.Column && not (isTypeSeqBlockElementContinuator token) -> -1

// This ensures we close a namespace body when we see the next namespace definition
//
@@ -1822,7 +1822,7 @@ type LexFilterImpl (
popCtxt()
reprocess()

| _, CtxtTypeDefns offsidePos :: _
| _, CtxtTypeDefns(offsidePos, _) :: _
when isSemiSemi || (if relaxWhitespace2OffsideRule || isTypeContinuator token then tokenStartCol + 1 else tokenStartCol) <= offsidePos.Column ->
if debug then dprintf "token at column %d is offside from TYPE(offsidePos=%a)! pop and reprocess\n" tokenStartCol outputPos offsidePos
popCtxt()
@@ -2076,8 +2076,9 @@ type LexFilterImpl (
pushCtxtSeqBlock tokenTup AddBlockEnd
returnToken tokenLexbufState token

| EQUALS, CtxtTypeDefns _ :: _ ->
| EQUALS, CtxtTypeDefns(p, _) :: _ ->
if debug then dprintf "CtxType: EQUALS, pushing CtxtSeqBlock\n"
replaceCtxt tokenTup (CtxtTypeDefns(p, Some tokenTup.EndPos))
pushCtxtSeqBlock tokenTup AddBlockEnd
returnToken tokenLexbufState token

@@ -2385,22 +2386,42 @@ type LexFilterImpl (
pushCtxt tokenTup (CtxtFun tokenStartPos)
returnToken tokenLexbufState OFUN

// type I = interface .... end
| INTERFACE, CtxtSeqBlock _ :: CtxtTypeDefns(typePos, Some equalsEndPos) :: _ when
(tokenTup.LastTokenPos = equalsEndPos &&

// Allow deindenting interface representation when started after `=`:
//
// type I = interface
// abstract P: int
// end

let allowDeindent = tokenTup.EndPos.Line = equalsEndPos.Line

let lookaheadTokenTup = peekNextTokenTup ()
let lookaheadTokenStartPos = startPosOfTokenTup lookaheadTokenTup
match lookaheadTokenTup.Token with
| END ->
lookaheadTokenStartPos.Column >= typePos.Column

| DEFAULT | OVERRIDE | INTERFACE | NEW | TYPE | STATIC | MEMBER | ABSTRACT | INHERIT | LBRACK_LESS ->
let limitPos = if allowDeindent then typePos else tokenTup.StartPos
lookaheadTokenStartPos.Column >= limitPos.Column + 1

| _ ->
false) ->

if debug then dprintf "INTERFACE, pushing CtxtParen, tokenStartPos = %a\n" outputPos tokenStartPos
pushCtxt tokenTup (CtxtParen (token, tokenStartPos))
pushCtxtSeqBlock tokenTup AddBlockEnd
returnToken tokenLexbufState token

// type C with interface .... with
// type C = interface .... with
| INTERFACE, _ ->
let lookaheadTokenTup = peekNextTokenTup()
let lookaheadTokenStartPos = startPosOfTokenTup lookaheadTokenTup
match lookaheadTokenTup.Token with
// type I = interface .... end
| DEFAULT | OVERRIDE | INTERFACE | NEW | TYPE | STATIC | END | MEMBER | ABSTRACT | INHERIT | LBRACK_LESS ->
if debug then dprintf "INTERFACE, pushing CtxtParen, tokenStartPos = %a, lookaheadTokenStartPos = %a\n" outputPos tokenStartPos outputPos lookaheadTokenStartPos
pushCtxt tokenTup (CtxtParen (token, tokenStartPos))
pushCtxtSeqBlock tokenTup AddBlockEnd
returnToken tokenLexbufState token
// type C with interface .... with
// type C = interface .... with
| _ ->
if debug then dprintf "INTERFACE, pushing CtxtInterfaceHead, tokenStartPos = %a, lookaheadTokenStartPos = %a\n" outputPos tokenStartPos outputPos lookaheadTokenStartPos
pushCtxt tokenTup (CtxtInterfaceHead tokenStartPos)
returnToken tokenLexbufState OINTERFACE_MEMBER
if debug then dprintf "INTERFACE, pushing CtxtInterfaceHead, tokenStartPos = %a, lookaheadTokenStartPos \n" outputPos tokenStartPos
pushCtxt tokenTup (CtxtInterfaceHead tokenStartPos)
returnToken tokenLexbufState OINTERFACE_MEMBER

| CLASS, _ ->
if debug then dprintf "CLASS, pushing CtxtParen(%a)\n" outputPos tokenStartPos
@@ -2411,7 +2432,7 @@ type LexFilterImpl (
| TYPE, _ ->
insertComingSoonTokens("TYPE", TYPE_COMING_SOON, TYPE_IS_HERE)
if debug then dprintf "TYPE, pushing CtxtTypeDefns(%a)\n" outputPos tokenStartPos
pushCtxt tokenTup (CtxtTypeDefns tokenStartPos)
pushCtxt tokenTup (CtxtTypeDefns(tokenStartPos, None))
pool.Return tokenTup
hwTokenFetch useBlockRule

16 changes: 12 additions & 4 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
@@ -1760,7 +1760,7 @@ tyconClassDefn:
| classOrInterfaceOrStruct classDefnBlock END
{ false, ($1, $2), Some(rhs2 parseState 1 3) }

| classOrInterfaceOrStruct classDefnBlock recover
| classOrInterfaceOrStruct classDefnBlock ends_coming_soon_or_recover
{ reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnmatchedClassInterfaceOrStruct())
let m = (rhs parseState 1, $2) ||> unionRangeWithListBy (fun (d: SynMemberDefn) -> d.Range)
false, ($1, $2), Some(m) }
@@ -2006,6 +2006,13 @@ classDefnMember:
| Some(mWithKwd, members, m) -> Some mWithKwd, Some members, unionRanges (rhs2 parseState 1 4) m
[ SynMemberDefn.Interface($4, mWithKwd, members, mWhole) ] }

| opt_attributes opt_access interfaceMember recover
{ let mInterface = rhs parseState 3
if not (isNil $1) then errorR(Error(FSComp.SR.parsAttributesAreNotPermittedOnInterfaceImplementations(), rhs parseState 1))
if Option.isSome $2 then errorR(Error(FSComp.SR.parsInterfacesHaveSameVisibilityAsEnclosingType(), mInterface))
let ty = SynType.FromParseError(mInterface.EndRange)
[ SynMemberDefn.Interface(ty, None, None, rhs2 parseState 1 3) ] }

| opt_attributes opt_access abstractMemberFlags opt_inline nameop opt_explicitValTyparDecls COLON topTypeWithTypeConstraints classMemberSpfnGetSet opt_ODECLEND
{ let ty, arity = $8
let isInline, doc, id, explicitValTyparDecls = (Option.isSome $4), grabXmlDoc(parseState, $1, 1), $5, $6
@@ -2293,9 +2300,10 @@ inheritsDefn:
SynMemberDefn.ImplicitInherit($2, $4, $5, mDecl) }

| INHERIT ends_coming_soon_or_recover
{ let mDecl = (rhs parseState 1)
if not $2 then errorR (Error(FSComp.SR.parsTypeNameCannotBeEmpty (), mDecl))
SynMemberDefn.Inherit(SynType.LongIdent(SynLongIdent([], [], [])), None, mDecl) }
{ let mInherit = rhs parseState 1
if not $2 then errorR (Error(FSComp.SR.parsTypeNameCannotBeEmpty (), mInherit))
let ty = SynType.FromParseError(mInherit.EndRange)
SynMemberDefn.Inherit(ty, None, mInherit) }

optAsSpec:
| asSpec
5 changes: 2 additions & 3 deletions tests/service/data/SyntaxTree/Member/Inherit 03.fs.bsl
Original file line number Diff line number Diff line change
@@ -11,9 +11,8 @@ ImplFile
false, None, (3,5--3,6)),
ObjectModel
(Unspecified,
[Inherit
(LongIdent (SynLongIdent ([], [], [])), None,
(4,4--4,11))], (4,4--4,11)), [], None, (3,5--4,11),
[Inherit (FromParseError (4,11--4,11), None, (4,4--4,11))],
(4,4--4,11)), [], None, (3,5--4,11),
{ LeadingKeyword = Type (3,0--3,4)
EqualsRange = Some (3,7--3,8)
WithKeyword = None })], (3,0--4,11))],
5 changes: 2 additions & 3 deletions tests/service/data/SyntaxTree/Member/Inherit 04.fs.bsl
Original file line number Diff line number Diff line change
@@ -11,9 +11,8 @@ ImplFile
false, None, (3,5--3,6)),
ObjectModel
(Unspecified,
[Inherit
(LongIdent (SynLongIdent ([], [], [])), None,
(4,4--4,11))], (4,4--4,11)), [], None, (3,5--4,11),
[Inherit (FromParseError (4,11--4,11), None, (4,4--4,11))],
(4,4--4,11)), [], None, (3,5--4,11),
{ LeadingKeyword = Type (3,0--3,4)
EqualsRange = Some (3,7--3,8)
WithKeyword = None })], (3,0--4,11));
4 changes: 1 addition & 3 deletions tests/service/data/SyntaxTree/Member/Inherit 05.fs.bsl
Original file line number Diff line number Diff line change
@@ -11,9 +11,7 @@ ImplFile
false, None, (3,5--3,6)),
ObjectModel
(Unspecified,
[Inherit
(LongIdent (SynLongIdent ([], [], [])), None,
(4,4--4,11));
[Inherit (FromParseError (4,11--4,11), None, (4,4--4,11));
Member
(SynBinding
(None, Normal, false, false, [],
6 changes: 6 additions & 0 deletions tests/service/data/SyntaxTree/Member/Inherit 08.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Module

type T =
inherit as t

member this.P = 1
47 changes: 47 additions & 0 deletions tests/service/data/SyntaxTree/Member/Inherit 08.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
ImplFile
(ParsedImplFileInput
("/root/Member/Inherit 08.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Types
([SynTypeDefn
(SynComponentInfo
([], None, [], [T],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
false, None, (3,5--3,6)),
ObjectModel
(Unspecified,
[Inherit (FromParseError (4,11--4,11), None, (4,4--4,11));
Member
(SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((4,12), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(Some { IsInstance = true
IsDispatchSlot = false
IsOverrideOrExplicitImpl = false
IsFinal = false
GetterOrSetterIsCompilerGenerated = false
MemberKind = Member },
SynValInfo
([[SynArgInfo ([], false, None)]; []],
SynArgInfo ([], false, None)), None, None),
LongIdent
(SynLongIdent
([this; P], [(6,15--6,16)], [None; None]), None,
None, Pats [], None, (6,11--6,17)), None,
Const (Int32 1, (6,20--6,21)), (6,11--6,17),
NoneAtInvisible,
{ LeadingKeyword = Member (6,4--6,10)
InlineKeyword = None
EqualsRange = Some (6,18--6,19) }), (4,12--6,21))],
(4,4--6,21)), [], None, (3,5--6,21),
{ LeadingKeyword = Type (3,0--3,4)
EqualsRange = Some (3,7--3,8)
WithKeyword = None })], (3,0--6,21))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--6,21), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(4,12)-(4,14) parse error Unexpected keyword 'as' in type definition
8 changes: 8 additions & 0 deletions tests/service/data/SyntaxTree/Member/Interface 08.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Module

type T =
interface

member this.P = 1

()
49 changes: 49 additions & 0 deletions tests/service/data/SyntaxTree/Member/Interface 08.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
ImplFile
(ParsedImplFileInput
("/root/Member/Interface 08.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Types
([SynTypeDefn
(SynComponentInfo
([], None, [], [T],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
false, None, (3,5--3,6)),
ObjectModel
(Unspecified,
[Interface
(FromParseError (4,13--4,13), None, None, (4,4--4,13));
Member
(SynBinding
(None, Normal, false, false, [],
PreXmlDoc ((6,4), FSharp.Compiler.Xml.XmlDocCollector),
SynValData
(Some { IsInstance = true
IsDispatchSlot = false
IsOverrideOrExplicitImpl = false
IsFinal = false
GetterOrSetterIsCompilerGenerated = false
MemberKind = Member },
SynValInfo
([[SynArgInfo ([], false, None)]; []],
SynArgInfo ([], false, None)), None, None),
LongIdent
(SynLongIdent
([this; P], [(6,15--6,16)], [None; None]), None,
None, Pats [], None, (6,11--6,17)), None,
Const (Int32 1, (6,20--6,21)), (6,11--6,17),
NoneAtInvisible,
{ LeadingKeyword = Member (6,4--6,10)
InlineKeyword = None
EqualsRange = Some (6,18--6,19) }), (6,4--6,21))],
(4,4--6,21)), [], None, (3,5--6,21),
{ LeadingKeyword = Type (3,0--3,4)
EqualsRange = Some (3,7--3,8)
WithKeyword = None })], (3,0--6,21));
Expr (Const (Unit, (8,0--8,2)), (8,0--8,2))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--8,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(4,14)-(6,4) parse error Incomplete structured construct at or before this point in member definition
8 changes: 8 additions & 0 deletions tests/service/data/SyntaxTree/Member/Interface 09.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Module

type T =
interface

type T2 = int

()
39 changes: 39 additions & 0 deletions tests/service/data/SyntaxTree/Member/Interface 09.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
ImplFile
(ParsedImplFileInput
("/root/Member/Interface 09.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Types
([SynTypeDefn
(SynComponentInfo
([], None, [], [T],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
false, None, (3,5--3,6)),
ObjectModel
(Unspecified,
[Interface
(FromParseError (4,13--4,13), None, None, (4,4--4,13))],
(4,4--4,13)), [], None, (3,5--4,13),
{ LeadingKeyword = Type (3,0--3,4)
EqualsRange = Some (3,7--3,8)
WithKeyword = None })], (3,0--4,13));
Types
([SynTypeDefn
(SynComponentInfo
([], None, [], [T2],
PreXmlDoc ((6,0), FSharp.Compiler.Xml.XmlDocCollector),
false, None, (6,5--6,7)),
Simple
(TypeAbbrev
(Ok, LongIdent (SynLongIdent ([int], [], [None])),
(6,10--6,13)), (6,10--6,13)), [], None, (6,5--6,13),
{ LeadingKeyword = Type (6,0--6,4)
EqualsRange = Some (6,8--6,9)
WithKeyword = None })], (6,0--6,13));
Expr (Const (Unit, (8,0--8,2)), (8,0--8,2))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--8,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(6,0)-(6,4) parse error Incomplete structured construct at or before this point in member definition
8 changes: 8 additions & 0 deletions tests/service/data/SyntaxTree/Member/Interface 10.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Module

type T =
interface

type T2 = int

()
39 changes: 39 additions & 0 deletions tests/service/data/SyntaxTree/Member/Interface 10.fs.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
ImplFile
(ParsedImplFileInput
("/root/Member/Interface 10.fs", false, QualifiedNameOfFile Module, [], [],
[SynModuleOrNamespace
([Module], false, NamedModule,
[Types
([SynTypeDefn
(SynComponentInfo
([], None, [], [T],
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector),
false, None, (3,5--3,6)),
ObjectModel
(Unspecified,
[Interface
(FromParseError (4,13--4,13), None, None, (4,4--4,13))],
(4,4--4,13)), [], None, (3,5--4,13),
{ LeadingKeyword = Type (3,0--3,4)
EqualsRange = Some (3,7--3,8)
WithKeyword = None })], (3,0--4,13));
Types
([SynTypeDefn
(SynComponentInfo
([], None, [], [T2],
PreXmlDoc ((6,0), FSharp.Compiler.Xml.XmlDocCollector),
false, None, (6,5--6,7)),
Simple
(TypeAbbrev
(Ok, LongIdent (SynLongIdent ([int], [], [None])),
(6,10--6,13)), (6,10--6,13)), [], None, (6,5--6,13),
{ LeadingKeyword = Type (6,0--6,4)
EqualsRange = Some (6,8--6,9)
WithKeyword = None })], (6,0--6,13));
Expr (Const (Unit, (8,0--8,2)), (8,0--8,2))],
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
(1,0--8,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(6,0)-(6,4) parse error Incomplete structured construct at or before this point in member definition
Loading