internal: avoid use of (*token.File).Name

When line directives are in use, we want the logical file name, not the
one we found the bytes in. This matters most for cgo, where the file we
parsed is not the one the user wants to see.

Updates golang/go#35720.

Change-Id: I495328071d8865e6895cb731467f1601f11e93db
Reviewed-on: https://go-review.googlesource.com/c/tools/+/208100
Run-TryBot: Heschi Kreinick <heschi@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 4ff2fcb..5ba18e2 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -450,7 +450,7 @@
 	if tok == nil {
 		return nil, nil, nil, errors.Errorf("no file for pos in package %s", searchpkg.ID())
 	}
-	uri := span.FileURI(tok.Name())
+	uri := span.FileURI(tok.Position(pos).Filename)
 
 	// Special case for ignored files.
 	var (
diff --git a/internal/span/token.go b/internal/span/token.go
index ce44541..01b5ed2 100644
--- a/internal/span/token.go
+++ b/internal/span/token.go
@@ -64,7 +64,7 @@
 	if f == nil {
 		return Span{}, fmt.Errorf("file not found in FileSet")
 	}
-	s := Span{v: span{URI: FileURI(f.Name())}}
+	s := Span{}
 	var err error
 	s.v.Start.Offset, err = offset(f, r.Start)
 	if err != nil {
@@ -76,6 +76,16 @@
 			return Span{}, err
 		}
 	}
+	// In the presence of line directives, a single File can have sections from
+	// multiple file names.
+	filename := f.Position(r.Start).Filename
+	if r.End.IsValid() {
+		if endFilename := f.Position(r.End).Filename; filename != endFilename {
+			return Span{}, fmt.Errorf("span begins in file %q but ends in %q", filename, endFilename)
+		}
+	}
+	s.v.URI = FileURI(filename)
+
 	s.v.Start.clean()
 	s.v.End.clean()
 	s.v.clean()