@@ -12,6 +12,10 @@ import (
12
12
"sync/atomic"
13
13
)
14
14
15
+ // If debug is set, invalid offset and position values cause a panic
16
+ // (go.dev/issue/57490).
17
+ const debug = false
18
+
15
19
// -----------------------------------------------------------------------------
16
20
// Positions
17
21
@@ -261,24 +265,54 @@ func (f *File) AddLineColumnInfo(offset int, filename string, line, column int)
261
265
f .mutex .Unlock ()
262
266
}
263
267
264
- // Pos returns the Pos value for the given file offset;
265
- // the offset must be <= f.Size().
268
+ // fixOffset fixes an out-of-bounds offset such that 0 <= offset <= f.size.
269
+ func (f * File ) fixOffset (offset int ) int {
270
+ switch {
271
+ case offset < 0 :
272
+ if ! debug {
273
+ return 0
274
+ }
275
+ case offset > f .size :
276
+ if ! debug {
277
+ return f .size
278
+ }
279
+ default :
280
+ return offset
281
+ }
282
+
283
+ // only generate this code if needed
284
+ if debug {
285
+ panic (fmt .Sprintf ("offset %d out of bounds [%d, %d] (position %d out of bounds [%d, %d])" ,
286
+ 0 /* for symmetry */ , offset , f .size ,
287
+ f .base + offset , f .base , f .base + f .size ))
288
+ }
289
+ return 0
290
+ }
291
+
292
+ // Pos returns the Pos value for the given file offset.
293
+ //
294
+ // If offset is negative, the result is the file's start
295
+ // position; if the offset is too large, the result is
296
+ // the file's end position (see also go.dev/issue/57490).
297
+ //
298
+ // The following invariant, though not true for Pos values
299
+ // in general, holds for the result p:
266
300
// f.Pos(f.Offset(p)) == p.
267
301
func (f * File ) Pos (offset int ) Pos {
268
- if offset > f .size {
269
- panic (fmt .Sprintf ("invalid file offset %d (should be <= %d)" , offset , f .size ))
270
- }
271
- return Pos (f .base + offset )
302
+ return Pos (f .base + f .fixOffset (offset ))
272
303
}
273
304
274
- // Offset returns the offset for the given file position p;
275
- // p must be a valid [Pos] value in that file.
276
- // f.Offset(f.Pos(offset)) == offset.
305
+ // Offset returns the offset for the given file position p.
306
+ //
307
+ // If p is before the file's start position (or if p is NoPos),
308
+ // the result is 0; if p is past the file's end position, the
309
+ // the result is the file size (see also go.dev/issue/57490).
310
+ //
311
+ // The following invariant, though not true for offset values
312
+ // in general, holds for the result offset:
313
+ // f.Offset(f.Pos(offset)) == offset
277
314
func (f * File ) Offset (p Pos ) int {
278
- if int (p ) < f .base || int (p ) > f .base + f .size {
279
- panic (fmt .Sprintf ("invalid Pos value %d (should be in [%d, %d])" , p , f .base , f .base + f .size ))
280
- }
281
- return int (p ) - f .base
315
+ return f .fixOffset (int (p ) - f .base )
282
316
}
283
317
284
318
// Line returns the line number for the given file position p;
@@ -330,27 +364,26 @@ func (f *File) unpack(offset int, adjusted bool) (filename string, line, column
330
364
}
331
365
332
366
func (f * File ) position (p Pos , adjusted bool ) (pos Position ) {
333
- offset := int (p ) - f .base
367
+ offset := f . fixOffset ( int (p ) - f .base )
334
368
pos .Offset = offset
335
369
pos .Filename , pos .Line , pos .Column = f .unpack (offset , adjusted )
336
370
return
337
371
}
338
372
339
373
// PositionFor returns the Position value for the given file position p.
374
+ // If p is out of bounds, it is adjusted to match the File.Offset behavior.
340
375
// If adjusted is set, the position may be adjusted by position-altering
341
376
// //line comments; otherwise those comments are ignored.
342
377
// p must be a Pos value in f or NoPos.
343
378
func (f * File ) PositionFor (p Pos , adjusted bool ) (pos Position ) {
344
379
if p != NoPos {
345
- if int (p ) < f .base || int (p ) > f .base + f .size {
346
- panic (fmt .Sprintf ("invalid Pos value %d (should be in [%d, %d])" , p , f .base , f .base + f .size ))
347
- }
348
380
pos = f .position (p , adjusted )
349
381
}
350
382
return
351
383
}
352
384
353
385
// Position returns the Position value for the given file position p.
386
+ // If p is out of bounds, it is adjusted to match the File.Offset behavior.
354
387
// Calling f.Position(p) is equivalent to calling f.PositionFor(p, true).
355
388
func (f * File ) Position (p Pos ) (pos Position ) {
356
389
return f .PositionFor (p , true )
0 commit comments