Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12013] ファイルの概要

このコミットは、Go言語のドキュメンテーションツールである godoc コマンドの挙動に関する修正です。具体的には、go doc foo のようにコマンドとして godoc を実行した際に、不必要なサジェスチョンが表示される問題を解決しています。この修正は、godoc がパスを処理する方法を改善し、特に go コマンドが絶対パスで godoc を呼び出す場合の挙動を調整することで実現されています。

コミット

  • コミットハッシュ: 6cdf0a1eab8c038b439543f45aff2bcf660b0eac
  • Author: Robert Griesemer gri@golang.org
  • Date: Fri Feb 17 11:01:16 2012 -0800

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/6cdf0a1eab8c038b439543f45aff2bcf660b0eac

元コミット内容

godoc: don't print spurious suggestion when running "go doc foo"

Fixes #3041.

R=rsc
CC=golang-dev
https://golang.org/cl/5671087

変更の背景

godoc はGo言語のパッケージやコマンドのドキュメンテーションを表示するためのツールです。通常、godoc <package_path> のようにパッケージのドキュメントを表示したり、godoc <symbol> のように特定のシンボルのドキュメントを表示したりします。また、go doc <command_name> のように go コマンドのサブコマンドとして実行されることもあります。

このコミットが修正しようとしている問題は、go doc foo のように godoc がコマンドとして実行された際に、godoc が不必要な「サジェスチョン」を表示してしまうというものでした。これは、godoc が入力された foo をパッケージパスとしても解釈しようとし、その結果、存在しないパッケージに対する「もしかしてこれですか?」のような誤った提案を出してしまうことが原因と考えられます。

特に、go コマンドが godoc を呼び出す際には、内部的に絶対パスを使用して godoc に引数を渡すことがあります。この場合、godoc はその絶対パスを「コマンド」としてではなく、「パッケージパス」として処理しようとし、結果として誤った挙動を引き起こしていました。コミットメッセージにある Fixes #3041 は、この問題がGoの内部的な課題追跡システムで管理されていたことを示唆しています。ただし、現在のGitHubリポジトリでは直接このIssueを見つけることはできませんでした。

この変更の目的は、godoc がコマンド引数をより適切に解釈し、特に go コマンドからの呼び出しにおいて、不必要なサジェスチョンや誤ったドキュメント表示を防ぐことにあります。

前提知識の解説

このコミットを理解するためには、以下のGo言語および godoc に関する基本的な知識が必要です。

  • godoc コマンド: Go言語のソースコードからドキュメンテーションを生成し、表示するためのツールです。パッケージ、関数、型、変数などのドキュメントを表示できます。また、HTTPサーバーとしてドキュメントを提供することも可能です。
  • go doc コマンド: go コマンドのサブコマンドの一つで、godoc ツールを呼び出してドキュメントを表示します。例えば、go doc fmtfmt パッケージのドキュメントを表示します。
  • filepath.IsAbs(path string) bool: Go言語の path/filepath パッケージにある関数で、与えられたパスが絶対パスであるかどうかを判定します。絶対パスは、ファイルシステムのルートからの完全なパスを指します(例: /usr/local/bin/go)。
  • abspath (absolute path): 絶対パスの略。ファイルシステム上の特定のリソースへの完全なパス。
  • relpath (relative path): 相対パスの略。現在の作業ディレクトリからの相対的なパス。
  • PageInfo: godoc 内部で使用される構造体で、表示するドキュメントページの情報を保持します。これには、ドキュメントの内容、タイトル、関連情報などが含まれます。
  • cmdHandlerpkgHandler: godoc 内部で、それぞれコマンドのドキュメントとパッケージのドキュメントを処理するためのハンドラ(ロジックの塊)です。godoc は、与えられた引数がコマンド名なのか、それともパッケージパスなのかを判断し、適切なハンドラを使ってドキュメントを検索・表示します。
  • getPageInfo(abspath, relpath, query string, mode PageInfoMode): cmdHandlerpkgHandler が持つメソッドで、指定されたパスやクエリに基づいて PageInfo オブジェクトを取得します。

godoc は、与えられた引数をまずパッケージパスとして解釈しようとし、次にコマンド名として解釈しようとする、という優先順位で動作していました。このコミットは、この解釈ロジックに filepath.IsAbs を用いた条件を追加することで、go コマンドからの呼び出し(絶対パスが渡される場合)の挙動を改善しています。

技術的詳細

このコミットの技術的な核心は、godoc がコマンド引数を処理する際のロジックに、パスが絶対パスであるかどうかのチェックを追加した点にあります。

修正前の godoc/main.go の関連部分では、まず pkgHandler.getPageInfo を使って引数をパッケージとして解釈しようとします。その後、cinfo := cmdHandler.getPageInfo(abspath, relpath, "", mode) の行で、無条件に引数をコマンドとして解釈しようとしていました。ここで abspath は、pkgHandler がパッケージとして解釈しようとした際に生成された絶対パスがそのまま使われていました。

問題は、go コマンドが godoc を呼び出す際に、例えば go doc foofoo が内部的に /path/to/go/bin/foo のような絶対パスとして godoc に渡される場合です。この場合、godocfoo をパッケージとしても、コマンドとしても解釈しようとします。しかし、foo が実際にはコマンド名であるにもかかわらず、godoc がそれを絶対パスとして受け取ると、cmdHandler.getPageInfo がその絶対パスを「コマンドの絶対パス」として処理しようとします。もしその絶対パスに該当するコマンドが存在しない場合、godoc は「もしかしてこれですか?」のような不必要なサジェスチョンを出力してしまうことがありました。

このコミットでは、cmdHandler.getPageInfo を呼び出す前に if !filepath.IsAbs(path) という条件を追加しています。

  • pathgodoc に渡された元の引数です。
  • filepath.IsAbs(path) は、その引数が絶対パスであるかどうかを判定します。

この条件が true (つまり、path が相対パスである) の場合にのみ、cmdHandler.getPageInfo を呼び出して引数をコマンドとして解釈しようとします。

これにより、go コマンドが godoc を絶対パスで呼び出した場合(例: go doc /usr/local/bin/go のような内部的な呼び出し)、filepath.IsAbs(path)true となり、!filepath.IsAbs(path)false になるため、cmdHandler.getPageInfo の呼び出しがスキップされます。これにより、godoc は絶対パスをコマンドとして誤って解釈しようとすることがなくなり、不必要なサジェスチョンの表示が抑制されます。

また、var cinfo PageInfo が追加され、cinfo の宣言が if ブロックの外に出されています。これは、cinfoif ブロックのスコープ外でも利用されるため、Goのスコープ規則に合わせた変更です。

コアとなるコードの変更箇所

--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -417,11 +417,13 @@ func main() {
 		info = pkgHandler.getPageInfo(abspath, relpath, "", mode)
 	}

-	// second, try as command
+	// second, try as command unless the path is absolute
+	// (the go command invokes godoc w/ absolute paths; don't override)
+	var cinfo PageInfo
 	if !filepath.IsAbs(path) {
 		abspath = absolutePath(path, cmdHandler.fsRoot)
+		cinfo = cmdHandler.getPageInfo(abspath, relpath, "", mode)
 	}
-	cinfo := cmdHandler.getPageInfo(abspath, relpath, "", mode)

 	// determine what to use
 	if info.IsEmpty() {

コアとなるコードの解説

変更の中心は、src/cmd/godoc/main.go ファイルの main 関数内の、コマンドとして引数を解釈しようとする部分です。

元のコード:

	// second, try as command
	cinfo := cmdHandler.getPageInfo(abspath, relpath, "", mode)

この行は、godoc に渡された引数を無条件にコマンドとして解釈しようとしていました。abspath は、その時点での絶対パス(パッケージとして解釈しようとした結果のパス)が使われていました。

変更後のコード:

	// second, try as command unless the path is absolute
	// (the go command invokes godoc w/ absolute paths; don't override)
	var cinfo PageInfo
	if !filepath.IsAbs(path) {
		abspath = absolutePath(path, cmdHandler.fsRoot)
		cinfo = cmdHandler.getPageInfo(abspath, relpath, "", mode)
	}

この変更により、以下の点が改善されました。

  1. var cinfo PageInfo の追加: cinfo 変数が if ブロックの外で宣言されるようになりました。これにより、if ブロックの条件が満たされない場合でも cinfo が定義され、その後の if info.IsEmpty() { ... } のロジックで cinfo を参照できるようになります。Goの言語仕様では、:= を使った短い変数宣言は、その変数が宣言されたブロック内でのみ有効です。この変更は、cinfo のスコープを適切に広げるためのものです。
  2. if !filepath.IsAbs(path) 条件の追加: これが最も重要な変更です。
    • pathgodoc コマンドに最初に渡された生の引数です。
    • filepath.IsAbs(path) は、その path が絶対パスであるかどうかをチェックします。
    • ! は論理否定なので、!filepath.IsAbs(path) は「path が絶対パスではない場合(つまり相対パスの場合)」という条件になります。
    • この条件が true の場合にのみ、godoc は引数をコマンドとして解釈しようとします。

このロジックにより、go コマンドが godoc を呼び出す際に、内部的に絶対パス(例: /usr/local/bin/go)を引数として渡した場合、filepath.IsAbs(path)true となり、if ブロック内の cmdHandler.getPageInfo の呼び出しはスキップされます。これにより、godoc は絶対パスを誤ってコマンド名として解釈しようとすることがなくなり、不必要なサジェスチョンが表示される問題が解決されます。

要するに、この修正は godoc が引数を「コマンド名」として解釈する前に、その引数が「相対パス」であるかどうかを確認するようになった、ということです。これにより、go コマンドのような上位のツールが godoc を絶対パスで呼び出す際の挙動がより適切に制御されるようになりました。

関連リンク

参考にした情報源リンク

  • path/filepath パッケージのドキュメント: https://pkg.go.dev/path/filepath
  • godoc コマンドに関する一般的な情報 (Go公式ドキュメントなど): https://go.dev/blog/godoc
  • Go言語のスコープに関する情報 (Go公式ドキュメントなど): https://go.dev/ref/spec#Declarations_and_scope
  • Issue #3041 については、直接的なGitHub上の情報は見つかりませんでした。これは、Goプロジェクトが内部的な課題追跡システムを使用していた時期のIssueであるか、または別のリポジトリのIssueである可能性があります。