internal/span: end of file is now last line +1

We already accepted it as a valid input, but for output we still produced
last_line,last_column+1
Also add package docs and improve error messages

Change-Id: I017aa326e8392134e67e8fabec47e750c8055454
Reviewed-on: https://go-review.googlesource.com/c/tools/+/199641
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/span/span.go b/internal/span/span.go
index b66bd7a..4d2ad09 100644
--- a/internal/span/span.go
+++ b/internal/span/span.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// Package span contains support for representing with positions and ranges in
+// text files.
 package span
 
 import (
@@ -34,6 +36,9 @@
 	Offset int `json:"offset"`
 }
 
+// Invalid is a span that reports false from IsValid
+var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}}
+
 var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}}
 
 // Converter is the interface to an object that can convert between line:column
diff --git a/internal/span/token.go b/internal/span/token.go
index 4efd0c8..ce44541 100644
--- a/internal/span/token.go
+++ b/internal/span/token.go
@@ -102,10 +102,10 @@
 	// go/token will panic if the offset is larger than the file's size,
 	// so check here to avoid panicking.
 	if s.Start().Offset() > converter.file.Size() {
-		return Range{}, fmt.Errorf("start offset %v is past the end of the file", s.Start())
+		return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), converter.file.Size())
 	}
 	if s.End().Offset() > converter.file.Size() {
-		return Range{}, fmt.Errorf("end offset %v is past the end of the file", s.End())
+		return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), converter.file.Size())
 	}
 	return Range{
 		FileSet: converter.fset,
@@ -116,10 +116,13 @@
 
 func (l *TokenConverter) ToPosition(offset int) (int, int, error) {
 	if offset > l.file.Size() {
-		return 0, 0, fmt.Errorf("offset %v is past the end of the file", offset)
+		return 0, 0, fmt.Errorf("offset %v is past the end of the file %v", offset, l.file.Size())
 	}
 	pos := l.file.Pos(offset)
 	p := l.fset.Position(pos)
+	if offset == l.file.Size() {
+		return p.Line + 1, 1, nil
+	}
 	return p.Line, p.Column, nil
 }
 
@@ -129,7 +132,7 @@
 	}
 	lineMax := l.file.LineCount() + 1
 	if line > lineMax {
-		return -1, fmt.Errorf("line is beyond end of file")
+		return -1, fmt.Errorf("line is beyond end of file %v", lineMax)
 	} else if line == lineMax {
 		if col > 1 {
 			return -1, fmt.Errorf("column is beyond end of file")
diff --git a/internal/span/token_test.go b/internal/span/token_test.go
index 7e5b467..c9fce77 100644
--- a/internal/span/token_test.go
+++ b/internal/span/token_test.go
@@ -24,14 +24,17 @@
 //
 //
 // file b.go
-package test
-`)},
+package test`)},
+	{"/c.go", []byte(`
+// file c.go
+package test`)},
 }
 
 var tokenTests = []span.Span{
 	span.New(span.FileURI("/a.go"), span.NewPoint(1, 1, 0), span.Point{}),
 	span.New(span.FileURI("/a.go"), span.NewPoint(3, 7, 20), span.NewPoint(3, 7, 20)),
 	span.New(span.FileURI("/b.go"), span.NewPoint(4, 9, 15), span.NewPoint(4, 13, 19)),
+	span.New(span.FileURI("/c.go"), span.NewPoint(4, 1, 26), span.Point{}),
 }
 
 func TestToken(t *testing.T) {
@@ -70,6 +73,6 @@
 	expected := fmt.Sprintf("%+v", expect)
 	got := fmt.Sprintf("%+v", gotLoc)
 	if expected != got {
-		t.Errorf("Expected %q got %q", expected, got)
+		t.Errorf("For %v expected %q got %q", in, expected, got)
 	}
 }