「go doc」コマンドが `package@version` 構文に対応し、モジュールキャッシュから任意バージョンのパッケージドキュメントを表示できるようになった。
go doc
概要
go doc コマンドが package@version という構文の引数を受け付けるようになった。例えば go doc example.com/pkg@v1.2.3 のように書くと、そのパッケージがワークスペースやGOPATH配下になくても、モジュールキャッシュから(必要であればダウンロードした上で)ドキュメントを取得して表示する。パッケージの短縮名(例: quote)、パッケージ名とシンボル名を分けた2引数形式(go doc quote@v1.0.0 Hello)、シンボル側に @version を付ける形式(go doc quote.Hello@v1.0.0)にも対応する。ただし @version はローカルインポート(./fooのような相対パス)や絶対パスの引数とは併用できない。
導入経緯
Issue #63696 で、ワークスペースに含まれないパッケージ(特にコマンド)のドキュメントも go doc から参照できるようにしてほしいという要望が出された。提案時点では、$PATH 上の実行ファイルの名前と、その実行ファイルに埋め込まれたビルド情報(module@version 形式のprovenance文字列)からバージョンを自動推測する案も併せて検討された。
議論のハイライト
- 自動推測案(
$PATH上のコマンド名とビルド情報からバージョンを推測する)には「魔法が効きすぎる」との懸念が示され、代わりにruntime/debug.ReadBuildInfoを使ったヘルパー関数の追加や、go versionへの専用フラグ追加といった代替案も議論された。 - rscが提案内容を整理し、
go doc txtar@v0.13.0/go doc txtar@v0.13.0 FS/go doc txtar.FS@v0.13.0のような形式を有効にする一方、go doc golang.org/x/tools/txtar@v0.13.0.FS(バージョン文字列の後にピリオドでシンボルを続ける形式)は無効とする方針を示し、likely acceptを経てaccepted(受理)となった。 - シンボル側に
@versionを付ける形式の実装は「既存の仕組みの大きな作り直しが必要」との理由でrsc・bcmillsが引き取り、コミュニティからのCL送付は控えるよう呼びかけられた。実装はCL 747380として提出された。
使用例
$ go doc rsc.io/quote@latest
$ go doc rsc.io/quote@v1.2.0
$ go doc quote@v1.2.0 # 短縮名でも可
$ go doc quote@v1.0.0 Hello # パッケージ+シンボルの2引数形式
$ go doc quote.Hello@v1.0.0 # シンボル側に@versionを付ける形式
移行時の注意
@version はローカルインポート(./foo のような相対パス)や絶対パスの引数とは併用できず、指定すると go doc はエラー終了する。バージョン指定なしの既存の呼び出しには影響しない。
実装解説
実装は cmd/go/internal/doc パッケージに集約されている。parseArgs は引数中の最初の @ で「パッケージ/シンボル部分」と「バージョン文字列」を分割し(doc.go:396)、ローカル/絶対パスとの併用はここで弾かれる(doc.go:406)。バージョン付きロードは loadVersioned が load.PackagesAndErrorsOutsideModule を呼び出すことで実現しており(mod.go:18)、ワークスペースの go.mod を汚さずにモジュールキャッシュから対象バージョンを取得する。
さらに、通常のパッケージ解決がすべて失敗した際の最後の手段として、$PATH 上の実行ファイルからバージョンを推測する inferVersion も実装されている(mod.go:34、呼び出し箇所はdoc.go:503)。これは exec.LookPath で実行ファイルを探し、debug/buildinfo.ReadFile でそのビルド情報からメインモジュールのパスとバージョンを読み取るというもので、issueコメントで「魔法が効きすぎる」と懸念されていたrscの当初案が、最終的にはフォールバック経路として採用された形になっている。