From 2b0619ca293420d1d026d4dd492c3a2913d588bf Mon Sep 17 00:00:00 2001
From: nojaf <florian.verdonck@outlook.com>
Date: Wed, 15 Feb 2023 12:22:35 +0100
Subject: [PATCH] Include end in nested module range.

---
 src/Compiler/pars.fsy                         | 26 ++++++++++------
 .../NestedModuleWithBeginEndAndDecls.fs       |  7 +++++
 .../NestedModuleWithBeginEndAndDecls.fs.bsl   | 30 +++++++++++++++++++
 .../NestedModule/RangeOfBeginEnd.fs           |  5 ++++
 .../NestedModule/RangeOfBeginEnd.fs.bsl       | 16 ++++++++++
 5 files changed, 75 insertions(+), 9 deletions(-)
 create mode 100644 tests/service/data/SyntaxTree/NestedModule/NestedModuleWithBeginEndAndDecls.fs
 create mode 100644 tests/service/data/SyntaxTree/NestedModule/NestedModuleWithBeginEndAndDecls.fs.bsl
 create mode 100644 tests/service/data/SyntaxTree/NestedModule/RangeOfBeginEnd.fs
 create mode 100644 tests/service/data/SyntaxTree/NestedModule/RangeOfBeginEnd.fs.bsl

diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy
index 7e515b0ac66..819b61d67f4 100644
--- a/src/Compiler/pars.fsy
+++ b/src/Compiler/pars.fsy
@@ -1273,12 +1273,17 @@ moduleDefn:
             | Some vis -> raiseParseErrorAt (rhs parseState 1) (FSComp.SR.parsIgnoreAttributesOnModuleAbbreviationAlwaysPrivate(vis.ToString()))
             | None -> ()
             [ SynModuleDecl.ModuleAbbrev(List.head path, eqn, (rhs parseState 3, eqn) ||> unionRangeWithListBy (fun id -> id.idRange) ) ]
-        | Choice2Of2 def -> 
+        | Choice2Of2 (def, mEndOpt) -> 
             if not (isSingleton path) then raiseParseErrorAt (rhs parseState 3) (FSComp.SR.parsModuleAbbreviationMustBeSimpleName())
             let info = SynComponentInfo(attribs @ attribs2, None, [], path, xmlDoc, false, vis, rhs parseState 3)
             let mEquals = rhs parseState 4
             let trivia: SynModuleDeclNestedModuleTrivia = { ModuleKeyword = Some mModule; EqualsRange = Some mEquals }
-            [ SynModuleDecl.NestedModule(info, isRec, def, false, ((rhs2 parseState 1 4, def) ||> unionRangeWithListBy (fun d -> d.Range) |> unionRangeWithXmlDoc xmlDoc), trivia)] }
+            let m =
+                (rhs2 parseState 1 4, def)
+                ||> unionRangeWithListBy (fun (d: SynModuleDecl) -> d.Range)
+                |> unionRangeWithXmlDoc xmlDoc
+            let m = match mEndOpt with | None -> m | Some mEnd -> unionRanges m mEnd
+            [ SynModuleDecl.NestedModule(info, isRec, def, false, m, trivia) ] }
 
   /* unattached custom attributes */
   | attributes recover
@@ -1339,17 +1344,18 @@ namedModuleDefnBlock:
          match $2 with 
          | [ SynModuleDecl.Expr (LongOrSingleIdent(false, SynLongIdent(path, _, _), None, _), _) ] -> 
              Choice1Of2  path
-         | _ -> 
-             Choice2Of2 $2 
+         | _ ->
+             Choice2Of2 ($2, None) 
        }
 
   | OBLOCKBEGIN moduleDefnsOrExpr recover 
      { // The lex filter ensures we can only get a mismatch in OBLOCKBEGIN/OBLOCKEND tokens if there was some other kind of error, hence we don't need to report this error
        // reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnClosedBlockInHashLight())   
-       Choice2Of2 $2 }
+       Choice2Of2 ($2, None) }
 
   | OBLOCKBEGIN error oblockend                
-     { Choice2Of2 [] }
+     { let mEnd = rhs parseState 3
+       Choice2Of2 ([], Some mEnd) }
 
   | wrappedNamedModuleDefn 
      { Choice2Of2 $1 }
@@ -1361,14 +1367,16 @@ namedModuleDefnBlock:
 /* A module definition that includes a 'begin'...'end' (rarely used in F# with #light syntax) */ 
 wrappedNamedModuleDefn:
   | structOrBegin moduleDefnsOrExprPossiblyEmpty END 
-     { $2 }
+     { let mEnd = rhs parseState 3
+       $2, Some mEnd }
 
   | structOrBegin moduleDefnsOrExprPossiblyEmpty recover 
      { reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsUnmatchedBeginOrStruct())  
-       $2 }
+       $2, None }
 
   | structOrBegin error END                      
-     { [] }
+     { let mEnd = rhs parseState 3
+       [], Some mEnd }
 
 
 tyconDefnAugmentation: 
diff --git a/tests/service/data/SyntaxTree/NestedModule/NestedModuleWithBeginEndAndDecls.fs b/tests/service/data/SyntaxTree/NestedModule/NestedModuleWithBeginEndAndDecls.fs
new file mode 100644
index 00000000000..dea32e54043
--- /dev/null
+++ b/tests/service/data/SyntaxTree/NestedModule/NestedModuleWithBeginEndAndDecls.fs
@@ -0,0 +1,7 @@
+namespace X
+
+module Y =
+    begin
+        let a = 0
+    end
+
diff --git a/tests/service/data/SyntaxTree/NestedModule/NestedModuleWithBeginEndAndDecls.fs.bsl b/tests/service/data/SyntaxTree/NestedModule/NestedModuleWithBeginEndAndDecls.fs.bsl
new file mode 100644
index 00000000000..2b6d2201624
--- /dev/null
+++ b/tests/service/data/SyntaxTree/NestedModule/NestedModuleWithBeginEndAndDecls.fs.bsl
@@ -0,0 +1,30 @@
+ImplFile
+  (ParsedImplFileInput
+     ("/root/NestedModule/NestedModuleWithBeginEndAndDecls.fs", false,
+      QualifiedNameOfFile NestedModuleWithBeginEndAndDecls, [], [],
+      [SynModuleOrNamespace
+         ([X], false, DeclaredNamespace,
+          [NestedModule
+             (SynComponentInfo
+                ([], None, [], [Y],
+                 PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false,
+                 None, (3,0--3,8)), false,
+              [Let
+                 (false,
+                  [SynBinding
+                     (None, Normal, false, false, [],
+                      PreXmlDoc ((5,8), FSharp.Compiler.Xml.XmlDocCollector),
+                      SynValData
+                        (None, SynValInfo ([], SynArgInfo ([], false, None)),
+                         None),
+                      Named (SynIdent (a, None), false, None, (5,12--5,13)),
+                      None, Const (Int32 0, (5,16--5,17)), (5,12--5,13),
+                      Yes (5,8--5,17), { LeadingKeyword = Let (5,8--5,11)
+                                         InlineKeyword = None
+                                         EqualsRange = Some (5,14--5,15) })],
+                  (5,8--5,17))], false, (3,0--6,7),
+              { ModuleKeyword = Some (3,0--3,6)
+                EqualsRange = Some (3,9--3,10) })], PreXmlDocEmpty, [], None,
+          (1,0--6,7), { LeadingKeyword = Namespace (1,0--1,9) })], (true, false),
+      { ConditionalDirectives = []
+        CodeComments = [] }, set []))
diff --git a/tests/service/data/SyntaxTree/NestedModule/RangeOfBeginEnd.fs b/tests/service/data/SyntaxTree/NestedModule/RangeOfBeginEnd.fs
new file mode 100644
index 00000000000..ea56b5fbb02
--- /dev/null
+++ b/tests/service/data/SyntaxTree/NestedModule/RangeOfBeginEnd.fs
@@ -0,0 +1,5 @@
+namespace X
+
+module Y =
+    begin end
+
diff --git a/tests/service/data/SyntaxTree/NestedModule/RangeOfBeginEnd.fs.bsl b/tests/service/data/SyntaxTree/NestedModule/RangeOfBeginEnd.fs.bsl
new file mode 100644
index 00000000000..aaa37f53971
--- /dev/null
+++ b/tests/service/data/SyntaxTree/NestedModule/RangeOfBeginEnd.fs.bsl
@@ -0,0 +1,16 @@
+ImplFile
+  (ParsedImplFileInput
+     ("/root/NestedModule/RangeOfBeginEnd.fs", false,
+      QualifiedNameOfFile RangeOfBeginEnd, [], [],
+      [SynModuleOrNamespace
+         ([X], false, DeclaredNamespace,
+          [NestedModule
+             (SynComponentInfo
+                ([], None, [], [Y],
+                 PreXmlDoc ((3,0), FSharp.Compiler.Xml.XmlDocCollector), false,
+                 None, (3,0--3,8)), false, [], false, (3,0--4,13),
+              { ModuleKeyword = Some (3,0--3,6)
+                EqualsRange = Some (3,9--3,10) })], PreXmlDocEmpty, [], None,
+          (1,0--4,13), { LeadingKeyword = Namespace (1,0--1,9) })],
+      (true, false), { ConditionalDirectives = []
+                       CodeComments = [] }, set []))
