From fda79a6038899711d7dd21f75d50a337c6098376 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 9 Jan 2019 15:34:05 +0000 Subject: [PATCH 1/8] poss fix --- src/fsharp/range.fs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index 7f31e032a9f..048b1dd2301 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -114,11 +114,18 @@ type FileIndexTable() = let mutable res = 0 let ok = fileToIndexTable.TryGetValue(f, &res) if ok then res + else + // remove relative parts from full path + let normalizedFilePath = if Path.IsPathRooted f then try Path.GetFullPath f with _ -> f else f + let ok = fileToIndexTable.TryGetValue(normalizedFilePath, &res) + if ok then res else lock fileToIndexTable (fun () -> let n = indexToFileTable.Count in - indexToFileTable.Add(f) - fileToIndexTable.[f] <- n + indexToFileTable.Add(normalizedFilePath) + fileToIndexTable.[normalizedFilePath] <- n + if f <> normalizedFilePath then + fileToIndexTable.[f] <- n n) member t.IndexToFile n = @@ -196,9 +203,7 @@ type range(code1:int64, code2: int64) = override r.ToString() = sprintf "%s (%d,%d--%d,%d) IsSynthetic=%b" r.FileName r.StartLine r.StartColumn r.EndLine r.EndColumn r.IsSynthetic let mkRange f b e = - // remove relative parts from full path - let normalizedFilePath = if Path.IsPathRooted f then try Path.GetFullPath f with _ -> f else f - range (fileIndexOfFile normalizedFilePath, b, e) + range (fileIndexOfFile f, b, e) let mkFileIndexRange fi b e = range (fi, b, e) From 8f7ec19dd08e9ac510f1568644bf07424e6d7017 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 9 Jan 2019 15:39:53 +0000 Subject: [PATCH 2/8] poss fix --- src/fsharp/range.fs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index 048b1dd2301..114cfca87ed 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -110,18 +110,24 @@ let _ = assert (isSyntheticMask = mask64 isSyntheticShift isSyntheticBitCount) type FileIndexTable() = let indexToFileTable = new ResizeArray<_>(11) let fileToIndexTable = new ConcurrentDictionary() + member t.FileToIndex f = let mutable res = 0 let ok = fileToIndexTable.TryGetValue(f, &res) if ok then res else - // remove relative parts from full path + // remove relative parts from full path to store the canonical entry let normalizedFilePath = if Path.IsPathRooted f then try Path.GetFullPath f with _ -> f else f - let ok = fileToIndexTable.TryGetValue(normalizedFilePath, &res) - if ok then res + let mutable res2 = 0 + let ok2 = fileToIndexTable.TryGetValue(normalizedFilePath, &res2) + if ok2 then + if f <> normalizedFilePath then + lock fileToIndexTable (fun () -> + fileToIndexTable.[f] <- res2) + res else lock fileToIndexTable (fun () -> - let n = indexToFileTable.Count in + let n = indexToFileTable.Count indexToFileTable.Add(normalizedFilePath) fileToIndexTable.[normalizedFilePath] <- n if f <> normalizedFilePath then From 7e264e0340e686bb87a8b044a0b028efb0e9dc3e Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 11 Jan 2019 11:48:23 +0000 Subject: [PATCH 3/8] Update range.fs --- src/fsharp/range.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index 114cfca87ed..203df459d02 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -124,7 +124,7 @@ type FileIndexTable() = if f <> normalizedFilePath then lock fileToIndexTable (fun () -> fileToIndexTable.[f] <- res2) - res + res2 else lock fileToIndexTable (fun () -> let n = indexToFileTable.Count From 16a69f1f506730958f83f447e1199c378500dc84 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 14 Jan 2019 12:22:42 +0000 Subject: [PATCH 4/8] Update range.fs --- src/fsharp/range.fs | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index 203df459d02..29f53a0f693 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -111,28 +111,38 @@ type FileIndexTable() = let indexToFileTable = new ResizeArray<_>(11) let fileToIndexTable = new ConcurrentDictionary() - member t.FileToIndex f = - let mutable res = 0 - let ok = fileToIndexTable.TryGetValue(f, &res) - if ok then res - else - // remove relative parts from full path to store the canonical entry - let normalizedFilePath = if Path.IsPathRooted f then try Path.GetFullPath f with _ -> f else f - let mutable res2 = 0 - let ok2 = fileToIndexTable.TryGetValue(normalizedFilePath, &res2) - if ok2 then + member t.FileToIndex filePath = + match fileToIndexTable.TryGetValue(filePath) with + | true, idx -> idx + | _ -> + + // remove relative parts from full path to store the normalized entry + let normalizedFilePath = try if isRooted then Path.GetFullPath filePath else filePath with _ -> filePath + match fileToIndexTable.TryGetValue(normalizedFilePath) with + | true, idx -> + // Record the non-normalized entry if necessary if f <> normalizedFilePath then lock fileToIndexTable (fun () -> - fileToIndexTable.[f] <- res2) - res2 - else + fileToIndexTable.[filePath] <- idx) + + // Return the index + idx + + | _ -> lock fileToIndexTable (fun () -> - let n = indexToFileTable.Count + // Get the new index + let idx = indexToFileTable.Count + + // Record the normalized entry indexToFileTable.Add(normalizedFilePath) - fileToIndexTable.[normalizedFilePath] <- n + fileToIndexTable.[normalizedFilePath] <- idx + + // Record the non-normalized entry if necessary if f <> normalizedFilePath then - fileToIndexTable.[f] <- n - n) + fileToIndexTable.[filePath] <- idx + + // Return the index + idx) member t.IndexToFile n = (if n < 0 then failwithf "fileOfFileIndex: negative argument: n = %d\n" n) From 1579549364938b9c34e5cc1a42da7b5d17a830ec Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 14 Jan 2019 12:28:54 +0000 Subject: [PATCH 5/8] fix build --- src/fsharp/range.fs | 9 +++++---- tests/service/EditorTests.fs | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index 29f53a0f693..e5f58ad4305 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -116,12 +116,13 @@ type FileIndexTable() = | true, idx -> idx | _ -> - // remove relative parts from full path to store the normalized entry - let normalizedFilePath = try if isRooted then Path.GetFullPath filePath else filePath with _ -> filePath + // Try again looking for a normalized entry. + // Remove relative parts from any full paths to store the normalized entry. + let normalizedFilePath = try if Path.IsPathRooted filePath then Path.GetFullPath filePath else filePath with _ -> filePath match fileToIndexTable.TryGetValue(normalizedFilePath) with | true, idx -> // Record the non-normalized entry if necessary - if f <> normalizedFilePath then + if filePath <> normalizedFilePath then lock fileToIndexTable (fun () -> fileToIndexTable.[filePath] <- idx) @@ -138,7 +139,7 @@ type FileIndexTable() = fileToIndexTable.[normalizedFilePath] <- idx // Record the non-normalized entry if necessary - if f <> normalizedFilePath then + if filePath <> normalizedFilePath then fileToIndexTable.[filePath] <- idx // Return the index diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 6579a72cc1b..aab6757010a 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -63,6 +63,10 @@ let ``Intro test`` () = let identToken = FSharpTokenTag.IDENT // let projectOptions = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously + // So we check that the messages are the same + for msg in typeCheckResults.Errors do + printfn "Got an error, hopefully with the right text: %A" msg + // We only expect one reported error. However, // on Unix, using filenames like /home/user/Test.fsx gives a second copy of all parse errors due to the // way the load closure for scripts is generated. So this returns two identical errors From b345cca3d79662fdd3645cd23c7ad0c5822db995 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 14 Jan 2019 14:25:57 +0000 Subject: [PATCH 6/8] docs and limit fix --- src/fsharp/range.fs | 114 ++++++++++++++++++++++++++--------- src/fsharp/range.fsi | 87 ++++++++++++++++++++++++-- tests/service/EditorTests.fs | 2 + 3 files changed, 171 insertions(+), 32 deletions(-) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index e5f58ad4305..1fdb39480db 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -5,7 +5,6 @@ module Microsoft.FSharp.Compiler.Range open System open System.IO -open System.Collections.Generic open System.Collections.Concurrent open Microsoft.FSharp.Core.Printf open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library @@ -16,17 +15,20 @@ type FileIndex = int32 [] let columnBitCount = 20 + [] let lineBitCount = 31 let posBitCount = lineBitCount + columnBitCount -let _ = assert (posBitCount <= 64) + let posColumnMask = mask64 0 columnBitCount + let lineColumnMask = mask64 columnBitCount lineBitCount [] [] type pos(code:int64) = + new (l, c) = let l = max 0 l let c = max 0 c @@ -35,63 +37,80 @@ type pos(code:int64) = pos p member p.Line = int32 (uint64 code >>> columnBitCount) + member p.Column = int32 (code &&& posColumnMask) member r.Encoding = code + static member EncodingSize = posBitCount + static member Decode (code:int64) : pos = pos code + override p.Equals(obj) = match obj with :? pos as p2 -> code = p2.Encoding | _ -> false + override p.GetHashCode() = hash code + override p.ToString() = sprintf "(%d,%d)" p.Line p.Column [] let fileIndexBitCount = 24 + [] let startColumnBitCount = columnBitCount // 20 + [] let endColumnBitCount = columnBitCount // 20 [] let startLineBitCount = lineBitCount // 31 + [] let heightBitCount = 27 + [] let isSyntheticBitCount = 1 -#if DEBUG -let _ = assert (fileIndexBitCount + startColumnBitCount + endColumnBitCount <= 64) -let _ = assert (startLineBitCount + heightBitCount + isSyntheticBitCount <= 64) -#endif - + [] let fileIndexShift = 0 + [] let startColumnShift = 24 + [] let endColumnShift = 44 [] let startLineShift = 0 + [] let heightShift = 31 + [] let isSyntheticShift = 58 - [] let fileIndexMask = 0b0000000000000000000000000000000000000000111111111111111111111111L + [] let startColumnMask = 0b0000000000000000000011111111111111111111000000000000000000000000L + [] let endColumnMask = 0b1111111111111111111100000000000000000000000000000000000000000000L [] let startLineMask = 0b0000000000000000000000000000000001111111111111111111111111111111L + [] let heightMask = 0b0000001111111111111111111111111110000000000000000000000000000000L + [] let isSyntheticMask = 0b0000010000000000000000000000000000000000000000000000000000000000L #if DEBUG +let _ = assert (posBitCount <= 64) +let _ = assert (fileIndexBitCount + startColumnBitCount + endColumnBitCount <= 64) +let _ = assert (startLineBitCount + heightBitCount + isSyntheticBitCount <= 64) + let _ = assert (startColumnShift = fileIndexShift + fileIndexBitCount) let _ = assert (endColumnShift = startColumnShift + startColumnBitCount) @@ -106,19 +125,33 @@ let _ = assert (endColumnMask = mask64 endColumnShift endColumnBitCount) let _ = assert (isSyntheticMask = mask64 isSyntheticShift isSyntheticBitCount) #endif -// This is just a standard unique-index table +/// Removes relative parts from any full paths +let normalizeFilePath filePath = + try + if Path.IsPathRooted filePath then + Path.GetFullPath filePath + else + filePath + with _ -> filePath + +/// A unique-index table for file names. type FileIndexTable() = let indexToFileTable = new ResizeArray<_>(11) let fileToIndexTable = new ConcurrentDictionary() - member t.FileToIndex filePath = + // Note: we should likely adjust this code to always normalize. However some testing (and possibly some + // product behaviour) appears to be sensitive to error messages reporting un-normalized file names. + // Currently all names going through 'mkRange' get normalized, while this going through just 'fileIndexOfFile' + // do not. Also any file names which are not put into ranges at all are non-normalized. + // + // TO move forward we should eventually introduce a new type NormalizedFileName that tracks this invariant. + member t.FileToIndex normalize filePath = match fileToIndexTable.TryGetValue(filePath) with | true, idx -> idx | _ -> // Try again looking for a normalized entry. - // Remove relative parts from any full paths to store the normalized entry. - let normalizedFilePath = try if Path.IsPathRooted filePath then Path.GetFullPath filePath else filePath with _ -> filePath + let normalizedFilePath = if normalize then normalizeFilePath filePath else filePath match fileToIndexTable.TryGetValue(normalizedFilePath) with | true, idx -> // Record the non-normalized entry if necessary @@ -135,7 +168,7 @@ type FileIndexTable() = let idx = indexToFileTable.Count // Record the normalized entry - indexToFileTable.Add(normalizedFilePath) + indexToFileTable.Add normalizedFilePath fileToIndexTable.[normalizedFilePath] <- idx // Record the non-normalized entry if necessary @@ -146,8 +179,10 @@ type FileIndexTable() = idx) member t.IndexToFile n = - (if n < 0 then failwithf "fileOfFileIndex: negative argument: n = %d\n" n) - (if n >= indexToFileTable.Count then failwithf "fileOfFileIndex: invalid argument: n = %d\n" n) + if n < 0 then + failwithf "fileOfFileIndex: negative argument: n = %d\n" n + if n >= indexToFileTable.Count then + failwithf "fileOfFileIndex: invalid argument: n = %d\n" n indexToFileTable.[n] let maxFileIndex = pown32 fileIndexBitCount @@ -157,8 +192,11 @@ let maxFileIndex = pown32 fileIndexBitCount let fileIndexTable = new FileIndexTable() // If we exceed the maximum number of files we'll start to report incorrect file names -let fileIndexOfFile f = fileIndexTable.FileToIndex(f) % maxFileIndex -let fileOfFileIndex n = fileIndexTable.IndexToFile(n) +let fileIndexOfFileAux normalize f = fileIndexTable.FileToIndex normalize f % maxFileIndex + +let fileIndexOfFile filePath = fileIndexOfFileAux false filePath + +let fileOfFileIndex idx = fileIndexTable.IndexToFile idx let mkPos l c = pos (l, c) @@ -182,19 +220,31 @@ type range(code1:int64, code2: int64) = new (fidx, b:pos, e:pos) = range(fidx, b.Line, b.Column, e.Line, e.Column) member r.StartLine = int32((code2 &&& startLineMask) >>> startLineShift) + member r.StartColumn = int32((code1 &&& startColumnMask) >>> startColumnShift) + member r.EndLine = int32((code2 &&& heightMask) >>> heightShift) + r.StartLine + member r.EndColumn = int32((code1 &&& endColumnMask) >>> endColumnShift) + member r.IsSynthetic = int32((code2 &&& isSyntheticMask) >>> isSyntheticShift) <> 0 + member r.Start = pos (r.StartLine, r.StartColumn) + member r.End = pos (r.EndLine, r.EndColumn) + member r.FileIndex = int32(code1 &&& fileIndexMask) + member m.StartRange = range (m.FileIndex, m.Start, m.Start) + member m.EndRange = range (m.FileIndex, m.End, m.End) + member r.FileName = fileOfFileIndex r.FileIndex + member r.MakeSynthetic() = range(code1, code2 ||| isSyntheticMask) member r.Code1 = code1 + member r.Code2 = code2 #if DEBUG @@ -219,28 +269,28 @@ type range(code1:int64, code2: int64) = override r.ToString() = sprintf "%s (%d,%d--%d,%d) IsSynthetic=%b" r.FileName r.StartLine r.StartColumn r.EndLine r.EndColumn r.IsSynthetic -let mkRange f b e = - range (fileIndexOfFile f, b, e) +let mkRange filePath startPos endPos = range (fileIndexOfFileAux true filePath, startPos, endPos) -let mkFileIndexRange fi b e = range (fi, b, e) +let mkFileIndexRange fileIndex startPos endPos = range (fileIndex, startPos, endPos) -(* end representation, start derived ops *) - let posOrder = Order.orderOn (fun (p:pos) -> p.Line, p.Column) (Pair.order (Int32.order, Int32.order)) -(* rangeOrder: not a total order, but enough to sort on ranges *) + +/// rangeOrder: not a total order, but enough to sort on ranges let rangeOrder = Order.orderOn (fun (r:range) -> r.FileName, r.Start) (Pair.order (String.order, posOrder)) let outputPos (os:TextWriter) (m:pos) = fprintf os "(%d,%d)" m.Line m.Column + let outputRange (os:TextWriter) (m:range) = fprintf os "%s%a-%a" m.FileName outputPos m.Start outputPos m.End -let boutputPos os (m:pos) = bprintf os "(%d,%d)" m.Line m.Column -let boutputRange os (m:range) = bprintf os "%s%a-%a" m.FileName boutputPos m.Start boutputPos m.End let posGt (p1:pos) (p2:pos) = (p1.Line > p2.Line || (p1.Line = p2.Line && p1.Column > p2.Column)) + let posEq (p1:pos) (p2:pos) = (p1.Line = p2.Line && p1.Column = p2.Column) + let posGeq p1 p2 = posEq p1 p2 || posGt p1 p2 + let posLt p1 p2 = posGt p2 p1 -// This is deliberately written in an allocation-free way, i.e. m1.Start, m1.End etc. are not called +/// This is deliberately written in an allocation-free way, i.e. m1.Start, m1.End etc. are not called let unionRanges (m1:range) (m2:range) = if m1.FileIndex <> m2.FileIndex then m2 else let b = @@ -264,9 +314,13 @@ let rangeBeforePos (m1:range) p = posGeq p m1.End let rangeN filename line = mkRange filename (mkPos line 0) (mkPos line 0) + let pos0 = mkPos 1 0 + let range0 = rangeN "unknown" 1 + let rangeStartup = rangeN "startup" 1 + let rangeCmdArgs = rangeN "commandLineArgs" 0 let trimRangeToLine (r:range) = @@ -280,6 +334,7 @@ let trimRangeToLine (r:range) = (* For Diagnostics *) let stringOfPos (pos:pos) = sprintf "(%d,%d)" pos.Line pos.Column + let stringOfRange (r:range) = sprintf "%s%s-%s" r.FileName (stringOfPos r.Start) (stringOfPos r.End) #if CHECK_LINE0_TYPES // turn on to check that we correctly transform zero-based line counts to one-based line counts @@ -294,17 +349,22 @@ type Pos01 = Line0 * int type Range01 = Pos01 * Pos01 module Line = + // Visual Studio uses line counts starting at 0, F# uses them starting at 1 let fromZ (line:Line0) = int line+1 + let toZ (line:int) : Line0 = LanguagePrimitives.Int32WithMeasure(line - 1) module Pos = + let fromZ (line:Line0) idx = mkPos (Line.fromZ line) idx - let toZ (p:pos) = (Line.toZ p.Line, p.Column) + let toZ (p:pos) = (Line.toZ p.Line, p.Column) module Range = + let toZ (m:range) = Pos.toZ m.Start, Pos.toZ m.End + let toFileZ (m:range) = m.FileName, toZ m diff --git a/src/fsharp/range.fsi b/src/fsharp/range.fsi index 5b3ea1e24d7..d4ef8be800f 100755 --- a/src/fsharp/range.fsi +++ b/src/fsharp/range.fsi @@ -10,44 +10,86 @@ open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler -(* we keep a global tables of filenames that we can reference by integers *) +/// An index into a global tables of filenames type FileIndex = int32 -val fileIndexOfFile : string -> FileIndex + +/// Convert a file path to an index +val fileIndexOfFile : filePath: string -> FileIndex + +/// Convert an index into a file path val fileOfFileIndex : FileIndex -> string +/// Represents a position in a file [] type pos = + + /// The line number for the position member Line : int + + /// The column number for the position member Column : int + /// The encoding of the position as a 64-bit integer member Encoding : int64 + + /// Decode a position fro a 64-bit integer static member Decode : int64 -> pos + /// The maximum number of bits needed to store an encoded position static member EncodingSize : int /// Create a position for the given line and column val mkPos : line:int -> column:int -> pos +/// Ordering on positions val posOrder : IComparer +/// Represents a range within a known file [] type range = + + /// The start line of the range member StartLine : int + + /// The start column of the range member StartColumn : int + + /// The line number for the end position of the range member EndLine : int + + /// The column number for the end position of the range member EndColumn : int + + /// The start position of the range member Start : pos + + /// The end position of the range member End : pos + + /// The empty range that is located at the start position of the range member StartRange: range + + /// The empty range that is located at the end position of the range member EndRange: range + + /// The file index for the range member FileIndex : int + + /// The file name for the file of the range member FileName : string + /// Synthetic marks ranges which are produced by intermediate compilation phases. This /// bit signifies that the range covers something that should not be visible to language /// service operations like dot-completion. member IsSynthetic : bool + + /// Convert a range to be synthetic member MakeSynthetic : unit -> range + + /// Convert a range to string member ToShortString : unit -> string + + /// The range where all values are zero static member Zero : range /// This view of range marks uses file indexes explicitly @@ -56,34 +98,61 @@ val mkFileIndexRange : FileIndex -> pos -> pos -> range /// This view hides the use of file indexes and just uses filenames val mkRange : string -> pos -> pos -> range +/// Reduce a range so it only covers a line val trimRangeToLine : range -> range /// not a total order, but enough to sort on ranges val rangeOrder : IComparer +/// Output a position val outputPos : System.IO.TextWriter -> pos -> unit + +/// Output a range val outputRange : System.IO.TextWriter -> range -> unit -val boutputPos : StringBuilder -> pos -> unit -val boutputRange : StringBuilder -> range -> unit +/// Compare positions for less-than val posLt : pos -> pos -> bool + +/// Compare positions for greater-than val posGt : pos -> pos -> bool + +/// Compare positions for equality val posEq : pos -> pos -> bool + +/// Compare positions for greater-than-or-equal-to val posGeq : pos -> pos -> bool +/// Union two ranges, taking their first occurring start position and last occurring end position val unionRanges : range -> range -> range + +/// Test to see if one range contains another range val rangeContainsRange : range -> range -> bool + +/// Test to see if a range contains a position val rangeContainsPos : range -> pos -> bool + +/// Test to see if a range occurs fully before a position val rangeBeforePos : range -> pos -> bool +/// Make a dummy range for a file val rangeN : string -> int -> range + +/// The zero position val pos0 : pos + +/// The zero range val range0 : range + +/// A range associated with a dummy file called "startup" val rangeStartup : range + +/// A range associated with a dummy file for the command line arguments val rangeCmdArgs : range -(* For diagnostics *) +/// Convert a position to a string val stringOfPos : pos -> string + +/// Convert a range to a string val stringOfRange : range -> string /// Represents a line number when using zero-based line counting (used by Visual Studio) @@ -98,22 +167,30 @@ type Line0 = int /// Represents a position using zero-based line counting (used by Visual Studio) type Pos01 = Line0 * int + /// Represents a range using zero-based line counting (used by Visual Studio) type Range01 = Pos01 * Pos01 module Line = + /// Convert a line number from zero-based line counting (used by Visual Studio) to one-based line counting (used internally in the F# compiler and in F# error messages) val fromZ : Line0 -> int + /// Convert a line number from one-based line counting (used internally in the F# compiler and in F# error messages) to zero-based line counting (used by Visual Studio) val toZ : int -> Line0 module Pos = + /// Convert a position from zero-based line counting (used by Visual Studio) to one-based line counting (used internally in the F# compiler and in F# error messages) val fromZ : line:Line0 -> column:int -> pos + /// Convert a position from one-based line counting (used internally in the F# compiler and in F# error messages) to zero-based line counting (used by Visual Studio) val toZ : pos -> Pos01 module Range = + /// Convert a range from one-based line counting (used internally in the F# compiler and in F# error messages) to zero-based line counting (used by Visual Studio) val toZ : range -> Range01 + + /// Convert a range from one-based line counting (used internally in the F# compiler and in F# error messages) to zero-based line counting (used by Visual Studio) val toFileZ : range -> string * Range01 diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index aab6757010a..81e66a7c20c 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -67,6 +67,8 @@ let ``Intro test`` () = for msg in typeCheckResults.Errors do printfn "Got an error, hopefully with the right text: %A" msg + printfn "typeCheckResults.Errors.Length = %d" typeCheckResults.Errors.Length + // We only expect one reported error. However, // on Unix, using filenames like /home/user/Test.fsx gives a second copy of all parse errors due to the // way the load closure for scripts is generated. So this returns two identical errors From 3b366e61b66c3c997b230fec84fc7c8011d01c90 Mon Sep 17 00:00:00 2001 From: Phillip Carter Date: Mon, 14 Jan 2019 17:17:58 +0000 Subject: [PATCH 7/8] Update src/fsharp/range.fs Co-Authored-By: dsyme --- src/fsharp/range.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index 1fdb39480db..126c2e01e83 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -126,7 +126,7 @@ let _ = assert (isSyntheticMask = mask64 isSyntheticShift isSyntheticBitCount) #endif /// Removes relative parts from any full paths -let normalizeFilePath filePath = +let normalizeFilePath (filePath: string) = try if Path.IsPathRooted filePath then Path.GetFullPath filePath From 8c4ef2a2fa6142c068cb29b46b271a83db252f56 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 14 Jan 2019 19:51:40 +0000 Subject: [PATCH 8/8] Update range.fs --- src/fsharp/range.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs index e6231b337ea..4e97edc2b37 100755 --- a/src/fsharp/range.fs +++ b/src/fsharp/range.fs @@ -128,7 +128,7 @@ let _ = assert (isSyntheticMask = mask64 isSyntheticShift isSyntheticBitCount) /// Removes relative parts from any full paths let normalizeFilePath (filePath: string) = try - if FileSystem.IsPathRooted filePath then + if FileSystem.IsPathRootedShim filePath then FileSystem.GetFullPathShim filePath else filePath