Skip to content

Commit db4dbc1

Browse files
authored
Parser: recover on unfinished nested modules (#15402)
1 parent d632aa4 commit db4dbc1

37 files changed

+409
-47
lines changed

src/Compiler/SyntaxTree/LexFilter.fs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ type Context =
4949
| CtxtTypeDefns of Position // 'type <here> =', not removed when we find the "="
5050

5151
| CtxtNamespaceHead of Position * token
52-
| CtxtModuleHead of Position * token * LexingModuleAttributes
52+
| CtxtModuleHead of Position * token * LexingModuleAttributes * isNested: bool
5353
| CtxtMemberHead of Position
5454
| CtxtMemberBody of Position
5555
// If bool is true then this is "whole file"
@@ -68,7 +68,7 @@ type Context =
6868

6969
member c.StartPos =
7070
match c with
71-
| CtxtNamespaceHead (p, _) | CtxtModuleHead (p, _, _) | CtxtException p | CtxtModuleBody (p, _) | CtxtNamespaceBody p
71+
| CtxtNamespaceHead (p, _) | CtxtModuleHead (p, _, _, _) | CtxtException p | CtxtModuleBody (p, _) | CtxtNamespaceBody p
7272
| CtxtLetDecl (_, p) | CtxtDo p | CtxtInterfaceHead p | CtxtTypeDefns p | CtxtParen (_, p) | CtxtMemberHead p | CtxtMemberBody p
7373
| CtxtWithAsLet p
7474
| CtxtWithAsAugment p
@@ -1393,6 +1393,9 @@ type LexFilterImpl (
13931393
| CtxtSeqBlock(_, _, AddOneSidedBlockEnd) ->
13941394
Some (ORIGHT_BLOCK_END(getLastTokenEndRange ()))
13951395

1396+
| CtxtModuleHead(isNested = true) ->
1397+
Some OBLOCKSEP
1398+
13961399
| _ ->
13971400
None
13981401

@@ -1598,11 +1601,11 @@ type LexFilterImpl (
15981601
// Otherwise it's a 'head' module declaration, so ignore it
15991602

16001603
// Here prevToken is either 'module', 'rec', 'global' (invalid), '.', or ident, because we skip attribute tokens and access modifier tokens
1601-
| _, CtxtModuleHead (moduleTokenPos, prevToken, lexingModuleAttributes) :: rest ->
1604+
| _, CtxtModuleHead (moduleTokenPos, prevToken, lexingModuleAttributes, isNested) :: rest ->
16021605
match prevToken, token with
16031606
| _, GREATER_RBRACK when lexingModuleAttributes = LexingModuleAttributes
16041607
&& moduleTokenPos.Column < tokenStartPos.Column ->
1605-
replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, prevToken, NotLexingModuleAttributes))
1608+
replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, prevToken, NotLexingModuleAttributes, isNested))
16061609
returnToken tokenLexbufState token
16071610
| _ when lexingModuleAttributes = LexingModuleAttributes
16081611
&& moduleTokenPos.Column < tokenStartPos.Column ->
@@ -1612,10 +1615,10 @@ type LexFilterImpl (
16121615
| MODULE, GLOBAL
16131616
| (MODULE | REC | DOT), (REC | IDENT _)
16141617
| IDENT _, DOT when moduleTokenPos.Column < tokenStartPos.Column ->
1615-
replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, token, NotLexingModuleAttributes))
1618+
replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, token, NotLexingModuleAttributes, isNested))
16161619
returnToken tokenLexbufState token
16171620
| MODULE, LBRACK_LESS when moduleTokenPos.Column < tokenStartPos.Column ->
1618-
replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, prevToken, LexingModuleAttributes))
1621+
replaceCtxt tokenTup (CtxtModuleHead (moduleTokenPos, prevToken, LexingModuleAttributes, isNested))
16191622
returnToken tokenLexbufState token
16201623
| _, (EQUALS | COLON) ->
16211624
if debug then dprintf "CtxtModuleHead: COLON/EQUALS, pushing CtxtModuleBody and CtxtSeqBlock\n"
@@ -1643,7 +1646,7 @@ type LexFilterImpl (
16431646
// and we've encountered declarations below
16441647
if debug then dprintf "CtxtModuleHead: not start of file, popping CtxtModuleHead\n"
16451648
popCtxt()
1646-
reprocessWithoutBlockRule()
1649+
insertTokenFromPrevPosToCurrentPos OBLOCKSEP
16471650

16481651
// Offside rule for SeqBlock.
16491652
// f x
@@ -1972,7 +1975,8 @@ type LexFilterImpl (
19721975
| MODULE, _ :: _ ->
19731976
insertComingSoonTokens("MODULE", MODULE_COMING_SOON, MODULE_IS_HERE)
19741977
if debug then dprintf "MODULE: entering CtxtModuleHead, awaiting EQUALS to go to CtxtSeqBlock (%a)\n" outputPos tokenStartPos
1975-
pushCtxt tokenTup (CtxtModuleHead (tokenStartPos, token, NotLexingModuleAttributes))
1978+
let isNested = match offsideStack with | [ CtxtSeqBlock _ ] -> false | _ -> true
1979+
pushCtxt tokenTup (CtxtModuleHead (tokenStartPos, token, NotLexingModuleAttributes, isNested))
19761980
pool.Return tokenTup
19771981
hwTokenFetch useBlockRule
19781982

src/Compiler/pars.fsy

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,12 @@ moduleIntro:
521521
let mModule = rhs parseState 1
522522
mModule, $4, $5.LongIdent, $3, $2 }
523523

524+
| moduleKeyword opt_attributes opt_access opt_rec OBLOCKSEP
525+
{ if not (isNil $2) then
526+
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AttributesToRightOfModuleKeyword (rhs parseState 4)
527+
let mModule = rhs parseState 1
528+
mModule, $4, [], $3, $2 }
529+
524530

525531
/* The start of a namespace declaration */
526532
namespaceIntro:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11

2-
neg41.fs(3,1,3,5): parse error FS0010: Unexpected keyword 'type' in definition. Expected '=' or other token.
2+
neg41.fs(2,20,3,1): parse error FS0010: Incomplete structured construct at or before this point in definition. Expected '=' or other token.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11

2-
neg42.fsi(3,1,3,5): parse error FS0010: Unexpected keyword 'type' in signature file. Expected ':', '=' or other token.
2+
neg42.fsi(2,20,3,1): parse error FS0010: Incomplete structured construct at or before this point in signature file. Expected ':', '=' or other token.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Module
2+
3+
module A
4+
5+
()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
ImplFile
2+
(ParsedImplFileInput
3+
("/root/ModuleOrNamespace/Nested module 03.fs", false,
4+
QualifiedNameOfFile Module, [], [],
5+
[SynModuleOrNamespace
6+
([Module], false, NamedModule,
7+
[NestedModule
8+
(SynComponentInfo
9+
([], None, [], [A],
10+
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false,
11+
None, (3,0--3,8)), false, [], false, (3,0--3,8),
12+
{ ModuleKeyword = Some (3,0--3,6)
13+
EqualsRange = None });
14+
Expr (Const (Unit, (5,0--5,2)), (5,0--5,2))],
15+
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
16+
(1,0--5,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
17+
{ ConditionalDirectives = []
18+
CodeComments = [] }, set []))
19+
20+
(3,9)-(5,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Module
2+
3+
module
4+
5+
()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
ImplFile
2+
(ParsedImplFileInput
3+
("/root/ModuleOrNamespace/Nested module 04.fs", false,
4+
QualifiedNameOfFile Module, [], [],
5+
[SynModuleOrNamespace
6+
([Module], false, NamedModule,
7+
[NestedModule
8+
(SynComponentInfo
9+
([], None, [], [],
10+
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false,
11+
None, (3,0--5,0)), false, [], false, (3,0--5,0),
12+
{ ModuleKeyword = Some (3,0--3,6)
13+
EqualsRange = None });
14+
Expr (Const (Unit, (5,0--5,2)), (5,0--5,2))],
15+
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
16+
(1,0--5,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
17+
{ ConditionalDirectives = []
18+
CodeComments = [] }, set []))
19+
20+
(3,7)-(5,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module Module
2+
3+
module rec
4+
5+
()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
ImplFile
2+
(ParsedImplFileInput
3+
("/root/ModuleOrNamespace/Nested module 05.fs", false,
4+
QualifiedNameOfFile Module, [], [],
5+
[SynModuleOrNamespace
6+
([Module], false, NamedModule,
7+
[NestedModule
8+
(SynComponentInfo
9+
([], None, [], [],
10+
PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false,
11+
None, (3,0--5,0)), true, [], false, (3,0--5,0),
12+
{ ModuleKeyword = Some (3,0--3,6)
13+
EqualsRange = None });
14+
Expr (Const (Unit, (5,0--5,2)), (5,0--5,2))],
15+
PreXmlDoc ((1,0), FSharp.Compiler.Xml.XmlDocCollector), [], None,
16+
(1,0--5,2), { LeadingKeyword = Module (1,0--1,6) })], (true, true),
17+
{ ConditionalDirectives = []
18+
CodeComments = [] }, set []))
19+
20+
(3,11)-(5,0) parse error Incomplete structured construct at or before this point in definition. Expected '=' or other token.

0 commit comments

Comments
 (0)