Reproducer (using the x/tools/go/packages testing API):

func TestReproducer(t *testing.T) {
    exported := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
        {
            Name: "test",
            Files: map[string]any{
                "test.go": `package test; import "test/other"; var _ = other.Test;`,
                "other/file.go": `package other

//line other.go:1:1
var Test int
`,
            },
        },
    })
    t.Cleanup(exported.Cleanup)

    exported.Config.Mode = packages.LoadSyntax
    exported.Config.Fset = token.NewFileSet()
    pkgs, err := packages.Load(exported.Config, "test")
    if err != nil {
        t.Fatal(err)
    }
    packages.PrintErrors(pkgs)

    p := pkgs[0].Imports["test/other"]
    pos := p.Types.Scope().Lookup("Test").Pos()
    t.Log(exported.Config.Fset.Position(pos)) // other.go:4:1
}

The Mode is set to packages.LoadSyntax, thus go/packages is going to load test/other from the ExportFile.

$ go test ./go/packages/ -run TestReproducer -v
=== RUN   TestReproducer
    (....)
    packages_test.go:3455: other.go:4:1
--- PASS: TestReproducer (0.15s)

The file name (other.go) comes from the line directive, but the line/col (4:1) information does not.

I think that the issue is somewhere in cmd/go, while debugging, i saw the same values here (i.e. other.go:4:1):

https://github.com/golang/tools/blob/4f820dbaf9859eaafa01a17d133583f4d8c5a73c/internal/gcimporter/ureader_yes.go#L201-L205

Comment From: gabyhelp

Related Issues

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

Comment From: ianlancetaylor

CC @griesemer @adonovan @findleyr

Comment From: adonovan

Thanks for investigating. Here's a smaller reproducer that doesn't depend on x/tools/go/packages:

xtools$ cat a/a.go 
package a

//line b.go:1:1
var A int
xtools$ go run ./go/gcexportdata/main.go $(go list -export -f {{.Export}} ./a)
package a
b.go:4:1: var A int

Adding logging to reader.pos in x/tools/internal/gcimporter/ureader_yes.go reveals that the export data contains (file "b.go", line 4, col 5) for this declaration, so the problem is on the encoding side, i.e. in the compiler.

The following change to the compiler fixes the problem:

// pos writes the position of p into the element bitstream.
func (w *writer) pos(p poser) {
    w.Sync(pkgbits.SyncPos)
    pos := p.Pos()

    // TODO(mdempsky): Track down the remaining cases here and fix them.
    if !w.Bool(pos.IsKnown()) {
        return
    }

    // TODO(mdempsky): Delta encoding.
    w.posBase(pos.Base())
-   w.Uint(pos.Line())
+   w.Uint(pos.RelLine())
-   w.Uint(pos.Col())
+   w.Uint(pos.RelCol())
}

but I'm not sure whether it is the right fix overall.

@griesemer