Skip to content

proposal: cmd/go: add FileHashes to go list -json output #78860

Description

@findleyr

I think this is one of perhaps a few missing features that can unlock greater reliability in go<->gopls interop.

Background

Tools that consume go list -json (notably gopls, via go/packages) receive file lists (GoFiles, CompiledGoFiles, etc.) but have no way to know what the go command actually saw in those files. Between the time go list reads a file and the time the tool reads it, the content may change. This makes it impossible to detect whether information derived from go list -export or -compiled is coherent with the source the tool subsequently parses.

gopls in particular wants to detect this incoherence so it can re-invoke the go command rather than present stale or inconsistent results.

The existing BuildID field (#37281) is a related package-level witness, but it is salted with the toolchain version and folds in flags and transitive dependencies, so it changes when source files do not; it also requires -export. A per-file, unsalted content hash is the missing complement.

Proposal

Prototype in CL 768880.

Add a new field to the Package struct reported by go list:

// FileHashes maps each source file listed above to the hex-encoded
// SHA-256 hash of its contents at the time go list read it.
FileHashes map[string]string

The map key is the file name exactly as it appears in the corresponding file list (relative to Dir for source files, absolute for generated CompiledGoFiles). The value is the lowercase hex encoding of the SHA-256 digest. Files that cannot be opened are omitted.

The field is populated only when explicitly requested via -json=FileHashes (or referenced from -f). It covers all files reported in the source-file lists, including test and ignored files.

When -overlay is in effect, the hash is of the overlay content.

Cost

When go list is invoked with -compiled, -export, or a request for Stale, the build action graph already computes the SHA-256 of every input file (cmd/go/internal/work.buildActionID, via cache.FileHash) to form the cache key. In those modes—precisely the modes go/packages uses when type information is needed—reporting FileHashes adds no additional I/O or hashing; it is a map lookup per file.

When none of those flags is set, requesting FileHashes requires reading each file in full (the package loader otherwise reads only the file header through the import block). This is an opt-in cost paid only by callers that ask for the field.

Compatibility

This adds a field; existing consumers are unaffected. The -json=field,... selector means the field is never computed or emitted unless requested.

CC @matloob @adonovan

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions