internal/span: switch to an offset function that doesn't panic

go/token.File.Offset panics on invalid positions, which is causing
crashes on invalid ASTs.

Change-Id: I6c698760ae92f3076a056c3560bea2e8eeb528db
Reviewed-on: https://go-review.googlesource.com/c/tools/+/169443
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/internal/span/token.go b/internal/span/token.go
index fb4d16d..0fc0169 100644
--- a/internal/span/token.go
+++ b/internal/span/token.go
@@ -65,12 +65,16 @@
 		return Span{}, fmt.Errorf("file not found in FileSet")
 	}
 	s := Span{v: span{URI: FileURI(f.Name())}}
-	if !r.Start.IsValid() {
-		return Span{}, fmt.Errorf("invalid position for start of range")
+	var err error
+	s.v.Start.Offset, err = offset(f, r.Start)
+	if err != nil {
+		return Span{}, err
 	}
-	s.v.Start.Offset = f.Offset(r.Start)
 	if r.End.IsValid() {
-		s.v.End.Offset = f.Offset(r.End)
+		s.v.End.Offset, err = offset(f, r.End)
+		if err != nil {
+			return Span{}, err
+		}
 	}
 	s.v.Start.clean()
 	s.v.End.clean()
@@ -79,6 +83,15 @@
 	return s.WithPosition(converter)
 }
 
+// offset is a copy of the Offset function in go/token, but with the adjustment
+// that it does not panic on invalid positions.
+func offset(f *token.File, pos token.Pos) (int, error) {
+	if int(pos) < f.Base() || int(pos) > f.Base()+f.Size() {
+		return 0, fmt.Errorf("invalid pos")
+	}
+	return int(pos) - f.Base(), nil
+}
+
 // Range converts a Span to a Range that represents the Span for the supplied
 // File.
 func (s Span) Range(converter *TokenConverter) (Range, error) {
@@ -121,5 +134,5 @@
 	// we assume that column is in bytes here, and that the first byte of a
 	// line is at column 1
 	pos += token.Pos(col - 1)
-	return l.file.Offset(pos), nil
+	return offset(l.file, pos)
 }