@@ -5,7 +5,6 @@ module Microsoft.FSharp.Compiler.Range
55
66open System
77open System.IO
8- open System.Collections .Generic
98open System.Collections .Concurrent
109open Microsoft.FSharp .Core .Printf
1110open Microsoft.FSharp .Compiler .AbstractIL .Internal .Library
@@ -16,17 +15,20 @@ type FileIndex = int32
1615
1716[<Literal>]
1817let columnBitCount = 20
18+
1919[<Literal>]
2020let lineBitCount = 31
2121
2222let posBitCount = lineBitCount+ columnBitCount
23- let _ = assert ( posBitCount <= 64 )
23+
2424let posColumnMask = mask640 columnBitCount
25+
2526let lineColumnMask = mask64 columnBitCount lineBitCount
2627
2728[<Struct; CustomEquality; NoComparison>]
2829[<System.Diagnostics.DebuggerDisplay( " {Line},{Column}" ) >]
2930type pos ( code : int64 ) =
31+
3032new ( l , c ) =
3133let l = max0 l
3234let c = max0 c
@@ -35,63 +37,80 @@ type pos(code:int64) =
3537 pos p
3638
3739member p.Line = int32( uint64 code>>> columnBitCount)
40+
3841member p.Column = int32( code&&& posColumnMask)
3942
4043member r.Encoding = code
44+
4145static member EncodingSize = posBitCount
46+
4247static member Decode ( code : int64 ) : pos = pos code
48+
4349override p.Equals ( obj ) = match objwith :? posas p2-> code= p2.Encoding| _ -> false
50+
4451override p.GetHashCode () = hash code
52+
4553override p.ToString () = sprintf" (%d ,%d )" p.Line p.Column
4654
4755[<Literal>]
4856let fileIndexBitCount = 24
57+
4958[<Literal>]
5059let startColumnBitCount = columnBitCount// 20
60+
5161[<Literal>]
5262let endColumnBitCount = columnBitCount// 20
5363
5464[<Literal>]
5565let startLineBitCount = lineBitCount// 31
66+
5667[<Literal>]
5768let heightBitCount = 27
69+
5870[<Literal>]
5971let isSyntheticBitCount = 1
60- #if DEBUG
61- let _ = assert ( fileIndexBitCount+ startColumnBitCount+ endColumnBitCount<= 64 )
62- let _ = assert ( startLineBitCount+ heightBitCount+ isSyntheticBitCount<= 64 )
63- #endif
64-
72+
6573[<Literal>]
6674let fileIndexShift = 0
75+
6776[<Literal>]
6877let startColumnShift = 24
78+
6979[<Literal>]
7080let endColumnShift = 44
7181
7282[<Literal>]
7383let startLineShift = 0
84+
7485[<Literal>]
7586let heightShift = 31
87+
7688[<Literal>]
7789let isSyntheticShift = 58
7890
79-
8091[<Literal>]
8192let fileIndexMask = 0b0000000000000000000000000000000000000000111111111111111111111111 L
93+
8294[<Literal>]
8395let startColumnMask = 0b0000000000000000000011111111111111111111000000000000000000000000 L
96+
8497[<Literal>]
8598let endColumnMask = 0b1111111111111111111100000000000000000000000000000000000000000000 L
8699
87100[<Literal>]
88101let startLineMask = 0b0000000000000000000000000000000001111111111111111111111111111111 L
102+
89103[<Literal>]
90104let heightMask = 0b0000001111111111111111111111111110000000000000000000000000000000 L
105+
91106[<Literal>]
92107let isSyntheticMask = 0b0000010000000000000000000000000000000000000000000000000000000000 L
93108
94109#if DEBUG
110+ let _ = assert ( posBitCount<= 64 )
111+ let _ = assert ( fileIndexBitCount+ startColumnBitCount+ endColumnBitCount<= 64 )
112+ let _ = assert ( startLineBitCount+ heightBitCount+ isSyntheticBitCount<= 64 )
113+
95114let _ = assert ( startColumnShift= fileIndexShift+ fileIndexBitCount)
96115let _ = assert ( endColumnShift= startColumnShift+ startColumnBitCount)
97116
@@ -106,24 +125,64 @@ let _ = assert (endColumnMask = mask64 endColumnShift endColumnBitCount)
106125let _ = assert ( isSyntheticMask= mask64 isSyntheticShift isSyntheticBitCount)
107126#endif
108127
109- // This is just a standard unique-index table
128+ /// Removes relative parts from any full paths
129+ let normalizeFilePath ( filePath : string ) =
130+ try
131+ if FileSystem.IsPathRootedShim filePaththen
132+ FileSystem.GetFullPathShim filePath
133+ else
134+ filePath
135+ with _ -> filePath
136+
137+ /// A unique-index table for file names.
110138type FileIndexTable () =
111139let indexToFileTable = new ResizeArray<_>( 11 )
112140let fileToIndexTable = new ConcurrentDictionary< string, int>()
113- member t.FileToIndex f =
114- let mutable res = 0
115- let ok = fileToIndexTable.TryGetValue( f, & res)
116- if okthen res
117- else
141+
142+ // Note: we should likely adjust this code to always normalize. However some testing (and possibly some
143+ // product behaviour) appears to be sensitive to error messages reporting un-normalized file names.
144+ // Currently all names going through 'mkRange' get normalized, while this going through just 'fileIndexOfFile'
145+ // do not. Also any file names which are not put into ranges at all are non-normalized.
146+ //
147+ // TO move forward we should eventually introduce a new type NormalizedFileName that tracks this invariant.
148+ member t.FileToIndex normalize filePath =
149+ match fileToIndexTable.TryGetValue( filePath) with
150+ | true , idx-> idx
151+ | _ ->
152+
153+ // Try again looking for a normalized entry.
154+ let normalizedFilePath = if normalizethen normalizeFilePath filePathelse filePath
155+ match fileToIndexTable.TryGetValue( normalizedFilePath) with
156+ | true , idx->
157+ // Record the non-normalized entry if necessary
158+ if filePath<> normalizedFilePaththen
159+ lock fileToIndexTable( fun () ->
160+ fileToIndexTable.[ filePath] <- idx)
161+
162+ // Return the index
163+ idx
164+
165+ | _ ->
118166 lock fileToIndexTable( fun () ->
119- let n = indexToFileTable.Countin
120- indexToFileTable.Add( f)
121- fileToIndexTable.[ f] <- n
122- n)
167+ // Get the new index
168+ let idx = indexToFileTable.Count
169+
170+ // Record the normalized entry
171+ indexToFileTable.Add normalizedFilePath
172+ fileToIndexTable.[ normalizedFilePath] <- idx
173+
174+ // Record the non-normalized entry if necessary
175+ if filePath<> normalizedFilePaththen
176+ fileToIndexTable.[ filePath] <- idx
177+
178+ // Return the index
179+ idx)
123180
124181member t.IndexToFile n =
125- ( if n< 0 then failwithf" fileOfFileIndex: negative argument: n =%d \n " n)
126- ( if n>= indexToFileTable.Countthen failwithf" fileOfFileIndex: invalid argument: n =%d \n " n)
182+ if n< 0 then
183+ failwithf" fileOfFileIndex: negative argument: n =%d \n " n
184+ if n>= indexToFileTable.Countthen
185+ failwithf" fileOfFileIndex: invalid argument: n =%d \n " n
127186 indexToFileTable.[ n]
128187
129188let maxFileIndex = pown32 fileIndexBitCount
@@ -133,8 +192,11 @@ let maxFileIndex = pown32 fileIndexBitCount
133192let fileIndexTable = new FileIndexTable()
134193
135194// If we exceed the maximum number of files we'll start to report incorrect file names
136- let fileIndexOfFile f = fileIndexTable.FileToIndex( f) % maxFileIndex
137- let fileOfFileIndex n = fileIndexTable.IndexToFile( n)
195+ let fileIndexOfFileAux normalize f = fileIndexTable.FileToIndex normalize f% maxFileIndex
196+
197+ let fileIndexOfFile filePath = fileIndexOfFileAuxfalse filePath
198+
199+ let fileOfFileIndex idx = fileIndexTable.IndexToFile idx
138200
139201let mkPos l c = pos( l, c)
140202
@@ -158,19 +220,31 @@ type range(code1:int64, code2: int64) =
158220new ( fidx , b : pos , e : pos ) = range( fidx, b.Line, b.Column, e.Line, e.Column)
159221
160222member r.StartLine = int32(( code2&&& startLineMask) >>> startLineShift)
223+
161224member r.StartColumn = int32(( code1&&& startColumnMask) >>> startColumnShift)
225+
162226member r.EndLine = int32(( code2&&& heightMask) >>> heightShift) + r.StartLine
227+
163228member r.EndColumn = int32(( code1&&& endColumnMask) >>> endColumnShift)
229+
164230member r.IsSynthetic = int32(( code2&&& isSyntheticMask) >>> isSyntheticShift) <> 0
231+
165232member r.Start = pos( r.StartLine, r.StartColumn)
233+
166234member r.End = pos( r.EndLine, r.EndColumn)
235+
167236member r.FileIndex = int32( code1&&& fileIndexMask)
237+
168238member m.StartRange = range( m.FileIndex, m.Start, m.Start)
239+
169240member m.EndRange = range( m.FileIndex, m.End, m.End)
241+
170242member r.FileName = fileOfFileIndex r.FileIndex
243+
171244member r.MakeSynthetic () = range( code1, code2||| isSyntheticMask)
172245
173246member r.Code1 = code1
247+
174248member r.Code2 = code2
175249
176250#if DEBUG
@@ -195,30 +269,28 @@ type range(code1:int64, code2: int64) =
195269
196270override r.ToString () = sprintf" %s (%d ,%d --%d ,%d ) IsSynthetic=%b " r.FileName r.StartLine r.StartColumn r.EndLine r.EndColumn r.IsSynthetic
197271
198- let mkRange f b e =
199- // remove relative parts from full path
200- let normalizedFilePath = if FileSystem.IsPathRootedShim fthen try FileSystem.GetFullPathShim fwith _ -> felse f
201- range( fileIndexOfFile normalizedFilePath, b, e)
272+ let mkRange filePath startPos endPos = range( fileIndexOfFileAuxtrue filePath, startPos, endPos)
202273
203- let mkFileIndexRange fi b e = range( fi , b , e )
274+ let mkFileIndexRange fileIndex startPos endPos = range( fileIndex , startPos , endPos )
204275
205- (* end representation, start derived ops*)
206-
207276let posOrder = Order.orderOn( fun ( p : pos ) -> p.Line, p.Column) ( Pair.order( Int32.order, Int32.order))
208- (* rangeOrder: not a total order, but enough to sort on ranges*)
277+
278+ /// rangeOrder: not a total order, but enough to sort on ranges
209279let rangeOrder = Order.orderOn( fun ( r : range ) -> r.FileName, r.Start) ( Pair.order( String.order, posOrder))
210280
211281let outputPos ( os : TextWriter ) ( m : pos ) = fprintf os" (%d ,%d )" m.Line m.Column
282+
212283let outputRange ( os : TextWriter ) ( m : range ) = fprintf os" %s%a -%a " m.FileName outputPos m.Start outputPos m.End
213- let boutputPos os ( m : pos ) = bprintf os" (%d ,%d )" m.Line m.Column
214- let boutputRange os ( m : range ) = bprintf os" %s%a -%a " m.FileName boutputPos m.Start boutputPos m.End
215284
216285let posGt ( p1 : pos ) ( p2 : pos ) = ( p1.Line> p2.Line|| ( p1.Line= p2.Line&& p1.Column> p2.Column))
286+
217287let posEq ( p1 : pos ) ( p2 : pos ) = ( p1.Line= p2.Line&& p1.Column= p2.Column)
288+
218289let posGeq p1 p2 = posEq p1 p2|| posGt p1 p2
290+
219291let posLt p1 p2 = posGt p2 p1
220292
221- // This is deliberately written in an allocation-free way, i.e. m1.Start, m1.End etc. are not called
293+ /// This is deliberately written in an allocation-free way, i.e. m1.Start, m1.End etc. are not called
222294let unionRanges ( m1 : range ) ( m2 : range ) =
223295if m1.FileIndex<> m2.FileIndexthen m2else
224296let b =
@@ -242,9 +314,13 @@ let rangeBeforePos (m1:range) p =
242314 posGeq p m1.End
243315
244316let rangeN filename line = mkRange filename( mkPos line0 ) ( mkPos line0 )
317+
245318let pos0 = mkPos1 0
319+
246320let range0 = rangeN" unknown" 1
321+
247322let rangeStartup = rangeN" startup" 1
323+
248324let rangeCmdArgs = rangeN" commandLineArgs" 0
249325
250326let trimRangeToLine ( r : range ) =
@@ -258,6 +334,7 @@ let trimRangeToLine (r:range) =
258334
259335(* For Diagnostics*)
260336let stringOfPos ( pos : pos ) = sprintf" (%d ,%d )" pos.Line pos.Column
337+
261338let stringOfRange ( r : range ) = sprintf" %s%s -%s " r.FileName( stringOfPos r.Start) ( stringOfPos r.End)
262339
263340#if CHECK_ LINE0_ TYPES// turn on to check that we correctly transform zero-based line counts to one-based line counts
@@ -272,17 +349,22 @@ type Pos01 = Line0 * int
272349type Range01 = Pos01* Pos01
273350
274351module Line =
352+
275353// Visual Studio uses line counts starting at 0, F# uses them starting at 1
276354let fromZ ( line : Line0 ) = int line+ 1
355+
277356let toZ ( line : int ) : Line0 = LanguagePrimitives.Int32WithMeasure( line- 1 )
278357
279358module Pos =
359+
280360let fromZ ( line : Line0 ) idx = mkPos( Line.fromZ line) idx
281- let toZ ( p : pos ) = ( Line.toZ p.Line, p.Column)
282361
362+ let toZ ( p : pos ) = ( Line.toZ p.Line, p.Column)
283363
284364module Range =
365+
285366let toZ ( m : range ) = Pos.toZ m.Start, Pos.toZ m.End
367+
286368let toFileZ ( m : range ) = m.FileName, toZ m
287369
288370