diff --git a/src/Compiler/Utilities/range.fs b/src/Compiler/Utilities/range.fs
index 6bf20edfc00..0a91bb1a2c3 100755
--- a/src/Compiler/Utilities/range.fs
+++ b/src/Compiler/Utilities/range.fs
@@ -492,6 +492,20 @@ module Range =
             else
                 m
 
+    let withStartEnd (startPos: Position) (endPos: Position) (r: range) = range (r.FileIndex, startPos, endPos)
+
+    let withStart (startPos: Position) (r: range) = range (r.FileIndex, startPos, r.End)
+
+    let withEnd (endPos: Position) (r: range) = range (r.FileIndex, r.Start, endPos)
+
+    let shiftStart (lineDelta: int) (columnDelta: int) (r: range) =
+        let shiftedStart = mkPos (r.Start.Line + lineDelta) (r.StartColumn + columnDelta)
+        range (r.FileIndex, shiftedStart, r.End)
+
+    let shiftEnd (lineDelta: int) (columnDelta: int) (r: range) =
+        let shiftedEnd = mkPos (r.End.Line + lineDelta) (r.EndColumn + columnDelta)
+        range (r.FileIndex, r.Start, shiftedEnd)
+
     let rangeContainsRange (m1: range) (m2: range) =
         m1.FileIndex = m2.FileIndex && posGeq m2.Start m1.Start && posGeq m1.End m2.End
 
diff --git a/src/Compiler/Utilities/range.fsi b/src/Compiler/Utilities/range.fsi
index 100c57fb67e..7b422fcd307 100755
--- a/src/Compiler/Utilities/range.fsi
+++ b/src/Compiler/Utilities/range.fsi
@@ -211,6 +211,21 @@ module Range =
 
     /// Union two ranges, taking their first occurring start position and last occurring end position
     val unionRanges: range -> range -> range
+    
+    // Create a new range with the given start and end positions
+    val withStartEnd: Position -> Position -> range -> range
+    
+    // Create a new range with the given start position
+    val withStart: Position -> range -> range
+    
+    // Create a new range with the given end position
+    val withEnd: Position -> range -> range
+    
+    // Create a new range with the start position shifted by the given deltas
+    val shiftStart: int -> int -> range -> range
+    
+    // Create a new range with the end position shifted by the given deltas
+    val shiftEnd: int -> int -> range -> range    
 
     /// Test to see if one range contains another range
     val rangeContainsRange: range -> range -> bool
diff --git a/src/Compiler/pars.fsy b/src/Compiler/pars.fsy
index d22e79fd15d..18e50749469 100644
--- a/src/Compiler/pars.fsy
+++ b/src/Compiler/pars.fsy
@@ -603,7 +603,7 @@ fileModuleSpec:
         | [] -> ParsedSigFileFragment.AnonModule($1, m)
         | _ ->
             let lastDeclRange = List.tryLast $1 |> Option.map (fun decl -> decl.Range) |> Option.defaultValue (rhs parseState 1)
-            let m = mkRange lastDeclRange.FileName (lhs parseState).Start lastDeclRange.End
+            let m = withStart (lhs parseState).Start lastDeclRange
             xml.MarkAsInvalid()
             let trivia: SynModuleOrNamespaceSigTrivia =
                 match mNamespaceOpt with
@@ -1159,7 +1159,7 @@ fileModuleImpl:
         | [], None -> ParsedImplFileFragment.AnonModule($1, m)
         | _ ->
             let lastDeclRange = List.tryLast $1 |> Option.map (fun decl -> decl.Range) |> Option.defaultValue (rhs parseState 1)
-            let m = mkRange lastDeclRange.FileName (lhs parseState).Start lastDeclRange.End
+            let m = withStart (lhs parseState).Start lastDeclRange
             xml.MarkAsInvalid()
             let trivia: SynModuleOrNamespaceTrivia =
                 match mNamespaceOpt with
@@ -4927,18 +4927,18 @@ atomicExprQualification:
   | GLOBAL
       { (fun e mLhs mDot ->
             reportParseErrorAt (rhs parseState 3) (FSComp.SR.nrGlobalUsedOnlyAsFirstName())
-            let fixedLhsm = mkRange mLhs.FileName mLhs.Start mDot.End // previous mLhs is wrong after 'recover'
+            let fixedLhsm = withEnd mDot.End mLhs // previous mLhs is wrong after 'recover'
             mkSynDotMissing mDot fixedLhsm e) }
 
   | /* empty */
       { (fun e mLhs mDot ->
             reportParseErrorAt mDot (FSComp.SR.parsMissingQualificationAfterDot())
-            let fixedLhsm = mkRange mLhs.FileName mLhs.Start mDot.End // previous mLhs is wrong after 'recover'
+            let fixedLhsm = withEnd mDot.End mLhs // previous mLhs is wrong after 'recover'
             mkSynDotMissing mDot fixedLhsm e) }
   | recover
       { (fun e mLhs mDot ->
             reportParseErrorAt mDot (FSComp.SR.parsMissingQualificationAfterDot())
-            let fixedLhsm = mkRange mLhs.FileName mLhs.Start mDot.End // previous mLhs is wrong after 'recover'
+            let fixedLhsm = withEnd mDot.End mLhs // previous mLhs is wrong after 'recover'
             // Include 'e' in the returned expression but throw it away
             mkSynDotMissing mDot fixedLhsm e) }
   | LPAREN COLON_COLON rparen DOT INT32
@@ -5883,7 +5883,7 @@ appTypeConPower:
     { if $2 <> "^" && $2 <> "^-" then reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnexpectedInfixOperator())
       if $2 = "^-" then
           let afterMinus = (rhs parseState 2).EndRange
-          let beforeMinus = mkRange afterMinus.FileName (mkPos afterMinus.EndLine (afterMinus.EndColumn - 1)) afterMinus.End
+          let beforeMinus = shiftStart 0 -1 afterMinus
           let m = unionRanges beforeMinus (rhs parseState 3)  // include MINUS in Negate range
           SynType.MeasurePower($1, SynRationalConst.Negate($3, m), lhs parseState)
       else SynType.MeasurePower($1, $3, lhs parseState) }
@@ -6044,7 +6044,7 @@ powerType:
      { if $2 <> "^" && $2 <> "^-" then reportParseErrorAt (rhs parseState 2) (FSComp.SR.parsUnexpectedInfixOperator())
        if $2 = "^-" then
            let afterMinus = (rhs parseState 2).EndRange
-           let beforeMinus = mkRange afterMinus.FileName (mkPos afterMinus.EndLine (afterMinus.EndColumn - 1)) afterMinus.End
+           let beforeMinus = shiftStart 0 -1 afterMinus
            let m = unionRanges beforeMinus (rhs parseState 3)  // include MINUS in Negate range
            SynType.MeasurePower($1, SynRationalConst.Negate($3, m), lhs parseState)
        else SynType.MeasurePower($1, $3, lhs parseState) }
@@ -6270,7 +6270,7 @@ measureTypePower:
        if $2 = "^-" then
            let mOp = rhs parseState 2
            let afterMinus = mOp.EndRange
-           let beforeMinus = mkRange afterMinus.FileName (mkPos afterMinus.EndLine (afterMinus.EndColumn - 1)) afterMinus.End
+           let beforeMinus = shiftStart 0 -1 afterMinus
            let mNegate = unionRanges beforeMinus (rhs parseState 3)  // include MINUS in Negate range
            let mCaret = unionRanges mOp.StartRange mNegate.StartRange
            SynMeasure.Power($1, mCaret, SynRationalConst.Negate($3, mNegate), lhs parseState)
diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl
index 7f07a0f1123..6a555b4aed0 100644
--- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl
+++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl
@@ -10039,8 +10039,13 @@ FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range range0
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range rangeCmdArgs
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range rangeN(System.String, Int32)
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range rangeStartup
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range shiftEnd(Int32, Int32, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range shiftStart(Int32, Int32, FSharp.Compiler.Text.Range)
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range trimRangeToLine(FSharp.Compiler.Text.Range)
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range unionRanges(FSharp.Compiler.Text.Range, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range withEnd(FSharp.Compiler.Text.Position, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range withStart(FSharp.Compiler.Text.Position, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range withStartEnd(FSharp.Compiler.Text.Position, FSharp.Compiler.Text.Position, FSharp.Compiler.Text.Range)
 FSharp.Compiler.Text.RangeModule: System.Collections.Generic.IComparer`1[FSharp.Compiler.Text.Position] get_posOrder()
 FSharp.Compiler.Text.RangeModule: System.Collections.Generic.IComparer`1[FSharp.Compiler.Text.Position] posOrder
 FSharp.Compiler.Text.RangeModule: System.Collections.Generic.IComparer`1[FSharp.Compiler.Text.Range] get_rangeOrder()
diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl
index 7f07a0f1123..6a555b4aed0 100644
--- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl
+++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl
@@ -10039,8 +10039,13 @@ FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range range0
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range rangeCmdArgs
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range rangeN(System.String, Int32)
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range rangeStartup
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range shiftEnd(Int32, Int32, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range shiftStart(Int32, Int32, FSharp.Compiler.Text.Range)
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range trimRangeToLine(FSharp.Compiler.Text.Range)
 FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range unionRanges(FSharp.Compiler.Text.Range, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range withEnd(FSharp.Compiler.Text.Position, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range withStart(FSharp.Compiler.Text.Position, FSharp.Compiler.Text.Range)
+FSharp.Compiler.Text.RangeModule: FSharp.Compiler.Text.Range withStartEnd(FSharp.Compiler.Text.Position, FSharp.Compiler.Text.Position, FSharp.Compiler.Text.Range)
 FSharp.Compiler.Text.RangeModule: System.Collections.Generic.IComparer`1[FSharp.Compiler.Text.Position] get_posOrder()
 FSharp.Compiler.Text.RangeModule: System.Collections.Generic.IComparer`1[FSharp.Compiler.Text.Position] posOrder
 FSharp.Compiler.Text.RangeModule: System.Collections.Generic.IComparer`1[FSharp.Compiler.Text.Range] get_rangeOrder()
diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj
index eb9294585d9..3b83200c1d6 100644
--- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj
+++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj
@@ -97,6 +97,9 @@
     <Compile Include="..\service\PrettyNaming.fs">
       <Link>PrettyNaming.fs</Link>
     </Compile>
+    <Compile Include="..\service\RangeTests.fs">
+      <Link>RangeTests.fs</Link>
+    </Compile>
     <Compile Include="TooltipTests.fs" />
     <Compile Include="..\service\Program.fs">
       <Link>Program.fs</Link>
diff --git a/tests/service/RangeTests.fs b/tests/service/RangeTests.fs
new file mode 100644
index 00000000000..2fd9d4c28fa
--- /dev/null
+++ b/tests/service/RangeTests.fs
@@ -0,0 +1,48 @@
+﻿module Tests.Service.RangeTests
+
+open FSharp.Compiler.Text
+open FSharp.Compiler.Text.Position
+open FSharp.Compiler.Text.Range
+open FsUnit
+open NUnit.Framework
+
+[<Test>]
+let ``withStartEnd Test`` () =
+    let z = Range.Zero
+    let newStart = mkPos 2 4
+    let newEnd = mkPos 7 23
+    let r = withStartEnd newStart newEnd z
+    r.Start |> shouldEqual newStart
+    r.End |> shouldEqual newEnd
+    
+[<Test>]
+let ``withStart Test`` () =
+    let z = Range.Zero
+    let newStart = mkPos 2 4
+    let r = withStart newStart z
+    r.Start |> shouldEqual newStart
+
+[<Test>]
+let ``withEnd Test`` () =
+    let z = Range.Zero
+    let newEnd = mkPos 2 4
+    let r = withEnd newEnd z
+    r.End |> shouldEqual newEnd
+
+[<Test>]
+let ``shiftStart Test`` () =
+    let z = Range.Zero
+    let lineDelta = 10
+    let columnDelta = 20
+    let r = shiftStart lineDelta columnDelta z
+    r.Start.Column |> shouldEqual (z.StartColumn + columnDelta)
+    r.Start.Line |> shouldEqual (z.StartLine + lineDelta)
+    
+[<Test>]
+let ``shiftEnd Test`` () =
+    let z = Range.Zero
+    let lineDelta = 10
+    let columnDelta = 20
+    let r = shiftEnd lineDelta columnDelta z
+    r.End.Column |> shouldEqual (z.EndColumn + columnDelta)
+    r.End.Line |> shouldEqual (z.EndLine + lineDelta)
