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

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

このコミットは、Go言語のツールチェインにおいて、パッケージのインポートに失敗した際に、そのエラーが発生したソースコード上の正確な位置(ファイル名と行番号)をエラーレポートに記録し、表示できるようにする改善を目的としています。これにより、開発者はインポートエラーの原因をより迅速に特定し、デバッグ作業を効率化できます。

コミット

commit e818536500ed22363d688ce2f5a024c60e899408
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Tue Jan 31 23:37:01 2012 +0100

    go: record location of failed imports for error reporting.
    
    Fixes #2664.
    
    R=rsc
    CC=golang-dev, remy
    https://golang.org/cl/5593047

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

https://github.com/golang/go/commit/e818536500ed22363d688ce2f5a024c60e899408

元コミット内容

Goツールチェインにおいて、インポートに失敗した場合のエラーレポートに、その失敗が発生したソースコード上の位置情報を記録するように変更します。これにより、エラーメッセージがより詳細になり、デバッグが容易になります。

この変更は、Issue #2664 を修正します。

変更の背景

Go言語の初期のバージョンでは、パッケージのインポートに失敗した場合のエラーメッセージは、どのパッケージがどのパッケージをインポートしようとして失敗したかというインポートスタック(ImportStack)は提供されていましたが、具体的にソースコードのどの行でそのインポートが記述されていたかという情報が欠けていました。

Issue #2664 は、この情報不足がデバッグを困難にしているという問題提起でした。例えば、複数のファイルで同じパッケージをインポートしている場合、エラーメッセージだけではどのファイルのどの行が問題の原因となっているのかを特定するのが難しいという課題がありました。

このコミットは、この問題を解決するために、インポートエラーが発生した際に、そのインポート文が記述されているソースコード上の正確な位置(ファイル名、行番号、列番号)をエラー情報に含めるようにGoツールチェインを改善することを目的としています。これにより、開発者はエラーメッセージを見ただけで、問題の箇所を直接特定できるようになり、デバッグの効率が大幅に向上します。

前提知識の解説

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

  • Goパッケージシステム: Go言語は、コードをパッケージという単位で管理します。import文を使って他のパッケージの機能を利用します。
  • go/buildパッケージ: Goの標準ライブラリの一部で、Goのソースコードを解析し、パッケージのビルド情報を取得するための機能を提供します。ScanDir関数は、指定されたディレクトリ内のGoソースファイルをスキャンし、パッケージ情報(インポートリストなど)を収集します。
  • go/tokenパッケージ: Goのソースコードのトークン(キーワード、識別子、演算子など)や、ソースコード上の位置(ファイル名、行番号、列番号)を扱うためのパッケージです。token.Position構造体は、ソースコード上の特定の位置を表します。
  • cmd/goツール: Go言語のビルド、テスト、依存関係管理などを行うためのコマンドラインツールです。go getコマンドやgo buildコマンドなどが含まれます。
  • importStack: Goツールチェインがパッケージの依存関係を追跡する際に使用する概念です。あるパッケージが別のパッケージをインポートし、そのパッケージがさらに別のパッケージをインポートするといった連鎖的なインポート関係をスタックとして保持し、エラー発生時にその経路を示すために利用されます。
  • PackageError: cmd/goツールがパッケージのロード中に発生したエラーを表現するために使用する構造体です。これには、インポートスタックとエラーメッセージが含まれます。

技術的詳細

このコミットの主要な変更点は、go/buildパッケージとcmd/goツールにおけるパッケージ情報の収集とエラーレポートの仕組みに、インポート位置の情報を追加したことです。

  1. DirInfo構造体の拡張:

    • src/pkg/go/build/dir.goにおいて、DirInfo構造体にImportPos map[string][]token.PositionTestImportPos map[string][]token.Positionという新しいフィールドが追加されました。
    • ImportPosは、通常のGoファイル(.go)でインポートされたパッケージのパスをキーとし、そのインポート文がソースコード上のどこに記述されているかを示すtoken.Positionのリストを値として保持します。
    • TestImportPosは、テストファイル(_test.go)における同様の情報を保持します。
    • これにより、ScanDir関数がディレクトリをスキャンする際に、単にインポートされたパッケージのリストだけでなく、そのインポートがどのファイルのどの行で発生したかという詳細な位置情報も収集できるようになります。
  2. ScanDir関数での位置情報の収集:

    • src/pkg/go/build/dir.goScanDir関数内で、importedtestImportedというマップの型がmap[string]boolからmap[string][]token.Positionに変更されました。
    • Goソースファイルを解析し、import文を見つけるたびに、fset.Position(spec.Pos())を使ってそのインポート文の開始位置(token.Position)を取得し、対応するマップに追記するように変更されました。これにより、一つのパッケージが複数の場所でインポートされている場合でも、すべての位置情報が記録されます。
  3. PackageError構造体の拡張とエラーメッセージの改善:

    • src/cmd/go/pkg.goにおいて、PackageError構造体にPos stringという新しいフィールドが追加されました。これは、エラーが発生したインポートの位置情報を文字列として保持します。
    • PackageErrorError()メソッドが変更され、Posフィールドが空でない場合に、インポートスタックに加えてこの位置情報もエラーメッセージに含めるようになりました。これにより、imports path/to/pkg: file.go:10: error messageのような、より詳細なエラーメッセージが生成されます。
  4. loadPackage関数での位置情報の伝播:

    • src/cmd/go/pkg.goloadPackage関数内で、パッケージのロード中にエラーが発生した場合、info.ImportPosDirInfoから取得したインポート位置情報)から該当するインポートの位置情報を取得し、p1.Error.Posに設定するロジックが追加されました。これにより、go/buildパッケージで収集された位置情報が、cmd/goツールで生成されるPackageErrorに適切に伝播されるようになります。
  5. テストの調整:

    • src/pkg/go/build/build_test.goTestBuild関数において、ScanDirのテストでImportPosTestImportPosの比較をスキップする行が追加されました。これは、テストの目的がDirInfoの基本的な情報収集の検証であり、インポート位置の詳細なテストは別の場所で行われるべきであるため、reflect.DeepEqualによる比較が失敗しないようにするための調整です。

これらの変更により、Goツールチェインはインポートエラー発生時に、そのエラーがソースコード上のどこで発生したかを正確に特定し、ユーザーに報告できるようになりました。

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

  • src/cmd/go/get.go:

    • errorf呼び出しでPackageErrorを初期化する際に、ImportStackErrを明示的に指定する形式に変更。
      -		errorf("%s", &PackageError{stk.copy(), err.Error()})
      +		errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()})
      
  • src/cmd/go/pkg.go:

    • PackageError構造体にPos stringフィールドを追加。
    • PackageError.Error()メソッドでPosが設定されていればエラーメッセージに含めるように変更。
      type PackageError struct {
      	ImportStack []string // shortest path from package named on command line to this one
      +	Pos         string   // position of error
      	Err         string   // the error itself
      }
      
      func (p *PackageError) Error() string {
      +	if p.Pos != "" {
      +		return strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Pos + ": " + p.Err
      +	}
      	return strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err
      }
      
    • loadPackage関数内で、p1.Errorが存在し、かつinfo.ImportPosに該当する情報があれば、p1.Error.Posに位置情報を設定するロジックを追加。
      		if p1.Error != nil {
      			if info.ImportPos != nil && len(info.ImportPos[path]) > 0 {
      				pos := info.ImportPos[path][0]
      				p1.Error.Pos = pos.String()
      			}
      		}
      
  • src/pkg/go/build/build_test.go:

    • TestBuild関数内で、ImportPosTestImportPosの比較をスキップする行を追加。
      +		// Don't bother testing import positions.
      +		tt.info.ImportPos, tt.info.TestImportPos = info.ImportPos, info.TestImportPos
      
  • src/pkg/go/build/dir.go:

    • DirInfo構造体にImportPos map[string][]token.PositionTestImportPos map[string][]token.Positionフィールドを追加。
      type DirInfo struct {
      	Package        string                      // Name of package in dir
      	PackageComment *ast.CommentGroup           // Package comments from GoFiles
      	ImportPath     string                      // Import path of package in dir
      	Imports        []string                    // All packages imported by GoFiles
      +	ImportPos      map[string][]token.Position // Source code location of imports
      
      	// Source files
      	GoFiles  []string // .go files in dir (excluding CgoFiles, TestGoFiles, XTestGoFiles)
      
      	TestGoFiles   []string // _test.go files in package
      	XTestGoFiles  []string // _test.go files outside package
      	TestImports   []string // All packages imported by (X)TestGoFiles
      +	TestImportPos map[string][]token.Position
      }
      
    • ScanDir関数内で、importedtestImportedマップの型をmap[string]boolからmap[string][]token.Positionに変更。
      -	imported := make(map[string]bool)
      -	testImported := make(map[string]bool)
      +	imported := make(map[string][]token.Position)
      +	testImported := make(map[string][]token.Position)
      
    • ScanDir関数内で、インポートパスをマップに追加する際に、token.Position情報も一緒に記録するように変更。
      			if isTest {
      -				testImported[path] = true
      +				testImported[path] = append(testImported[path], fset.Position(spec.Pos()))
      			} else {
      -				imported[path] = true
      +				imported[path] = append(imported[path], fset.Position(spec.Pos()))
      			}
      
    • ScanDir関数内で、DirInfo.ImportPosDirInfo.TestImportPosに収集したマップを代入。
      	di.Imports = make([]string, len(imported))
      +	di.ImportPos = imported
      	i := 0
      	for p := range imported {
      		di.Imports[i] = p
      		i++
      	}
      	di.TestImports = make([]string, len(testImported))
      +	di.TestImportPos = testImported
      	i = 0
      	for p := range testImported {
      		di.TestImports[i] = p
      

コアとなるコードの解説

このコミットの核心は、Goのビルドプロセスにおけるエラーレポートの精度向上にあります。

  1. DirInfoの拡張: go/buildパッケージのDirInfoは、特定のディレクトリ内のGoパッケージに関するメタデータ(パッケージ名、インポートリストなど)を保持する構造体です。このコミットでは、ImportPosTestImportPosという新しいフィールドが追加されました。これらは、インポートされた各パッケージがソースコードのどの位置(ファイル名、行番号、列番号)で宣言されているかを記録するためのマップです。これにより、パッケージの依存関係だけでなく、その依存関係がコード上でどのように表現されているかという詳細な情報が利用可能になります。

  2. ScanDirでの位置情報収集: ScanDir関数は、Goソースファイルを解析し、import文を検出します。変更前は、単にインポートされたパッケージのパスをリストに追加するだけでしたが、変更後はtoken.Positionを使ってインポート文の正確な位置を取得し、ImportPosまたはTestImportPosマップに記録するようになりました。これにより、go/buildパッケージの段階で、インポートに関する詳細な位置情報が収集されるようになります。

  3. PackageErrorの改善: cmd/goツールがパッケージのロード中にエラーを報告する際に使用するPackageError構造体にPosフィールドが追加されました。このフィールドは、エラーが発生したインポート文のソースコード上の位置を文字列として保持します。PackageErrorError()メソッドがこのPosフィールドを利用するように変更されたことで、生成されるエラーメッセージに「ファイル名:行番号:列番号」のような形式で位置情報が含まれるようになり、開発者はエラーの発生源を直接特定できるようになります。

  4. エラー情報の伝播: cmd/goloadPackage関数は、go/buildパッケージから取得したパッケージ情報(DirInfo)を利用してパッケージをロードします。この関数内で、もしパッケージのロード中にエラーが発生した場合、DirInfoに格納されているImportPosから該当するインポートの位置情報を取得し、それをPackageErrorPosフィールドに設定するロジックが追加されました。これにより、go/buildで収集された詳細な位置情報が、最終的なエラーレポートに適切に反映されるようになります。

これらの変更は、Goツールチェインが提供するエラーメッセージの質を向上させ、開発者がインポート関連の問題をより効率的にデバッグできるようにするための重要な改善です。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語のツールチェインにおいて、パッケージのインポートに失敗した際に、そのエラーが発生したソースコード上の正確な位置(ファイル名と行番号)をエラーレポートに記録し、表示できるようにする改善を目的としています。これにより、開発者はインポートエラーの原因をより迅速に特定し、デバッグ作業を効率化できます。

コミット

commit e818536500ed22363d688ce2f5a024c60e899408
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Tue Jan 31 23:37:01 2012 +0100

    go: record location of failed imports for error reporting.
    
    Fixes #2664.
    
    R=rsc
    CC=golang-dev, remy
    https://golang.org/cl/5593047

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

https://github.com/golang/go/commit/e818536500ed22363d688ce2f5a024c60e899408

元コミット内容

Goツールチェインにおいて、インポートに失敗した場合のエラーレポートに、その失敗が発生したソースコード上の位置情報を記録するように変更します。これにより、エラーメッセージがより詳細になり、デバッグが容易になります。

この変更は、Issue #2664 を修正します。

変更の背景

Go言語の初期のバージョンでは、パッケージのインポートに失敗した場合のエラーメッセージは、どのパッケージがどのパッケージをインポートしようとして失敗したかというインポートスタック(ImportStack)は提供されていましたが、具体的にソースコードのどの行でそのインポートが記述されていたかという情報が欠けていました。

Issue #2664 は、この情報不足がデバッグを困難にしているという問題提起でした。例えば、複数のファイルで同じパッケージをインポートしている場合、エラーメッセージだけではどのファイルのどの行が問題の原因となっているのかを特定するのが難しいという課題がありました。

このコミットは、この問題を解決するために、インポートエラーが発生した際に、そのインポート文が記述されているソースコード上の正確な位置(ファイル名、行番号、列番号)をエラー情報に含めるようにGoツールチェインを改善することを目的としています。これにより、開発者はエラーメッセージを見ただけで、問題の箇所を直接特定できるようになり、デバッグの効率が大幅に向上します。

前提知識の解説

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

  • Goパッケージシステム: Go言語は、コードをパッケージという単位で管理します。import文を使って他のパッケージの機能を利用します。
  • go/buildパッケージ: Goの標準ライブラリの一部で、Goのソースコードを解析し、パッケージのビルド情報を取得するための機能を提供します。ScanDir関数は、指定されたディレクトリ内のGoソースファイルをスキャンし、パッケージ情報(インポートリストなど)を収集します。
  • go/tokenパッケージ: Goのソースコードのトークン(キーワード、識別子、演算子など)や、ソースコード上の位置(ファイル名、行番号、列番号)を扱うためのパッケージです。token.Position構造体は、ソースコード上の特定の位置を表します。
  • cmd/goツール: Go言語のビルド、テスト、依存関係管理などを行うためのコマンドラインツールです。go getコマンドやgo buildコマンドなどが含まれます。
  • importStack: Goツールチェインがパッケージの依存関係を追跡する際に使用する概念です。あるパッケージが別のパッケージをインポートし、そのパッケージがさらに別のパッケージをインポートするといった連鎖的なインポート関係をスタックとして保持し、エラー発生時にその経路を示すために利用されます。
  • PackageError: cmd/goツールがパッケージのロード中に発生したエラーを表現するために使用する構造体です。これには、インポートスタックとエラーメッセージが含まれます。

技術的詳細

このコミットの主要な変更点は、go/buildパッケージとcmd/goツールにおけるパッケージ情報の収集とエラーレポートの仕組みに、インポート位置の情報を追加したことです。

  1. DirInfo構造体の拡張:

    • src/pkg/go/build/dir.goにおいて、DirInfo構造体にImportPos map[string][]token.PositionTestImportPos map[string][]token.Positionという新しいフィールドが追加されました。
    • ImportPosは、通常のGoファイル(.go)でインポートされたパッケージのパスをキーとし、そのインポート文がソースコード上のどこに記述されているかを示すtoken.Positionのリストを値として保持します。
    • TestImportPosは、テストファイル(_test.go)における同様の情報を保持します。
    • これにより、ScanDir関数がディレクトリをスキャンする際に、単にインポートされたパッケージのリストだけでなく、そのインポートがどのファイルのどの行で発生したかという詳細な位置情報も収集できるようになります。
  2. ScanDir関数での位置情報の収集:

    • src/pkg/go/build/dir.goScanDir関数内で、importedtestImportedというマップの型がmap[string]boolからmap[string][]token.Positionに変更されました。
    • Goソースファイルを解析し、import文を見つけるたびに、fset.Position(spec.Pos())を使ってそのインポート文の開始位置(token.Position)を取得し、対応するマップに追記するように変更されました。これにより、一つのパッケージが複数の場所でインポートされている場合でも、すべての位置情報が記録されます。
  3. PackageError構造体の拡張とエラーメッセージの改善:

    • src/cmd/go/pkg.goにおいて、PackageError構造体にPos stringという新しいフィールドが追加されました。これは、エラーが発生したインポートの位置情報を文字列として保持します。
    • PackageErrorError()メソッドが変更され、Posフィールドが空でない場合に、インポートスタックに加えてこの位置情報もエラーメッセージに含めるようになりました。これにより、imports path/to/pkg: file.go:10: error messageのような、より詳細なエラーメッセージが生成されます。
  4. loadPackage関数での位置情報の伝播:

    • src/cmd/go/pkg.goloadPackage関数内で、パッケージのロード中にエラーが発生した場合、info.ImportPosDirInfoから取得したインポート位置情報)から該当するインポートの位置情報を取得し、p1.Error.Posに設定するロジックが追加されました。これにより、go/buildパッケージで収集された位置情報が、cmd/goツールで生成されるPackageErrorに適切に伝播されるようになります。
  5. テストの調整:

    • src/pkg/go/build/build_test.goTestBuild関数において、ScanDirのテストでImportPosTestImportPosの比較をスキップする行が追加されました。これは、テストの目的がDirInfoの基本的な情報収集の検証であり、インポート位置の詳細なテストは別の場所で行われるべきであるため、reflect.DeepEqualによる比較が失敗しないようにするための調整です。

これらの変更により、Goツールチェインはインポートエラー発生時に、そのエラーがソースコード上のどこで発生したかを正確に特定し、ユーザーに報告できるようになりました。

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

  • src/cmd/go/get.go:

    • errorf呼び出しでPackageErrorを初期化する際に、ImportStackErrを明示的に指定する形式に変更。
      -		errorf("%s", &PackageError{stk.copy(), err.Error()})
      +		errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()})
      
  • src/cmd/go/pkg.go:

    • PackageError構造体にPos stringフィールドを追加。
    • PackageError.Error()メソッドでPosが設定されていればエラーメッセージに含めるように変更。
      type PackageError struct {
      	ImportStack []string // shortest path from package named on command line to this one
      +	Pos         string   // position of error
      	Err         string   // the error itself
      }
      
      func (p *PackageError) Error() string {
      +	if p.Pos != "" {
      +		return strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Pos + ": " + p.Err
      +	}
      	return strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err
      }
      
    • loadPackage関数内で、p1.Errorが存在し、かつinfo.ImportPosに該当する情報があれば、p1.Error.Posに位置情報を設定するロジックを追加。
      		if p1.Error != nil {
      			if info.ImportPos != nil && len(info.ImportPos[path]) > 0 {
      				pos := info.ImportPos[path][0]
      				p1.Error.Pos = pos.String()
      			}
      		}
      
  • src/pkg/go/build/build_test.go:

    • TestBuild関数内で、ImportPosTestImportPosの比較をスキップする行を追加。
      +		// Don't bother testing import positions.
      +		tt.info.ImportPos, tt.info.TestImportPos = info.ImportPos, info.TestImportPos
      
  • src/pkg/go/build/dir.go:

    • DirInfo構造体にImportPos map[string][]token.PositionTestImportPos map[string][]token.Positionフィールドを追加。
      type DirInfo struct {
      	Package        string                      // Name of package in dir
      	PackageComment *ast.CommentGroup           // Package comments from GoFiles
      	ImportPath     string                      // Import path of package in dir
      	Imports        []string                    // All packages imported by GoFiles
      +	ImportPos      map[string][]token.Position // Source code location of imports
      
      	// Source files
      	GoFiles  []string // .go files in dir (excluding CgoFiles, TestGoFiles, XTestGoFiles)
      
      	TestGoFiles   []string // _test.go files in package
      	XTestGoFiles  []string // _test.go files outside package
      	TestImports   []string // All packages imported by (X)TestGoFiles
      +	TestImportPos map[string][]token.Position
      }
      
    • ScanDir関数内で、importedtestImportedマップの型をmap[string]boolからmap[string][]token.Positionに変更。
      -	imported := make(map[string]bool)
      -	testImported := make(map[string]bool)
      +	imported := make(map[string][]token.Position)
      +	testImported := make(map[string][]token.Position)
      
    • ScanDir関数内で、インポートパスをマップに追加する際に、token.Position情報も一緒に記録するように変更。
      			if isTest {
      -				testImported[path] = true
      +				testImported[path] = append(testImported[path], fset.Position(spec.Pos()))
      			} else {
      -				imported[path] = true
      +				imported[path] = append(imported[path], fset.Position(spec.Pos()))
      			}
      
    • ScanDir関数内で、DirInfo.ImportPosDirInfo.TestImportPosに収集したマップを代入。
      	di.Imports = make([]string, len(imported))
      +	di.ImportPos = imported
      	i := 0
      	for p := range imported {
      		di.Imports[i] = p
      		i++
      	}
      	di.TestImports = make([]string, len(testImported))
      +	di.TestImportPos = testImported
      	i = 0
      	for p := range testImported {
      		di.TestImports[i] = p
      

コアとなるコードの解説

このコミットの核心は、Goのビルドプロセスにおけるエラーレポートの精度向上にあります。

  1. DirInfoの拡張: go/buildパッケージのDirInfoは、特定のディレクトリ内のGoパッケージに関するメタデータ(パッケージ名、インポートリストなど)を保持する構造体です。このコミットでは、ImportPosTestImportPosという新しいフィールドが追加されました。これらは、インポートされた各パッケージがソースコードのどの位置(ファイル名、行番号、列番号)で宣言されているかを記録するためのマップです。これにより、パッケージの依存関係だけでなく、その依存関係がコード上でどのように表現されているかという詳細な情報が利用可能になります。

  2. ScanDirでの位置情報収集: ScanDir関数は、Goソースファイルを解析し、import文を検出します。変更前は、単にインポートされたパッケージのパスをリストに追加するだけでしたが、変更後はtoken.Positionを使ってインポート文の正確な位置を取得し、ImportPosまたはTestImportPosマップに記録するようになりました。これにより、go/buildパッケージの段階で、インポートに関する詳細な位置情報が収集されるようになります。

  3. PackageErrorの改善: cmd/goツールがパッケージのロード中にエラーを報告する際に使用するPackageError構造体にPosフィールドが追加されました。このフィールドは、エラーが発生したインポート文のソースコード上の位置を文字列として保持します。PackageErrorError()メソッドがこのPosフィールドを利用するように変更されたことで、生成されるエラーメッセージに「ファイル名:行番号:列番号」のような形式で位置情報が含まれるようになり、開発者はエラーの発生源を直接特定できるようになります。

  4. エラー情報の伝播: cmd/goloadPackage関数は、go/buildパッケージから取得したパッケージ情報(DirInfo)を利用してパッケージをロードします。この関数内で、もしパッケージのロード中にエラーが発生した場合、DirInfoに格納されているImportPosから該当するインポートの位置情報を取得し、それをPackageErrorPosフィールドに設定するロジックが追加されました。これにより、go/buildで収集された詳細な位置情報が、最終的なエラーレポートに適切に反映されるようになります。

これらの変更は、Goツールチェインが提供するエラーメッセージの質を向上させ、開発者がインポート関連の問題をより効率的にデバッグできるようにするための重要な改善です。

関連リンク

参考にした情報源リンク