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

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

このコミットは、Go言語のcmd/goツールにおけるvetコマンドのバグ修正に関するものです。具体的には、go vetが一部のGoファイルを二重に処理してしまう問題と、パッケージディレクトリ外で実行された場合にファイルが見つからない問題を解決しています。

コミット

commit 11884db3d76a7e91521bb63cff3cc701c0c07826
Author: Russ Cox <rsc@golang.org>
Date:   Thu Feb 14 15:00:51 2013 -0500

    cmd/go: fix vet
    
    The IgnoredGoFiles are already listed in allgofiles,
    so they were being run twice. Worse, the ones in
    IgnoredGoFiles are not fully qualified paths, so they
    weren't being found when executed outside the
    package directory.
    
    Fixes #4764.
    
    R=golang-dev, minux.ma, franciscossouza
    CC=golang-dev
    https://golang.org/cl/7308049

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

https://github.com/golang/go/commit/11884db3d76a7e91521bb63cff3cc701c0c07826

元コミット内容

cmd/go: fix vet

IgnoredGoFilesはすでにallgofilesにリストされているため、二重に実行されていました。さらに悪いことに、IgnoredGoFiles内のファイルは完全修飾パスではなかったため、パッケージディレクトリ外で実行された場合に見つかりませんでした。

Fixes #4764.

変更の背景

この変更は、Goのvetコマンドが特定の条件下で正しく動作しないバグ(Issue #4764)を修正するために行われました。Issue #4764は、「cmd/go: vet fails with 'no such file or directory'」というタイトルで報告されており、go vetコマンドがcrypto/randパッケージ内のrand_windows.goのようなファイルを検出できず、「no such file or directory」というエラーで終了するという問題が指摘されていました。

この問題の根本原因は、go vetが処理するファイルリストの構築方法にありました。具体的には、pkg.allgofilespkg.IgnoredGoFilesという2つのリストが関係していました。pkg.allgofilesはパッケージ内のすべてのGoファイルのリストを含んでいましたが、pkg.IgnoredGoFilesもまた、ビルド時に無視されるべきファイル(例えば、特定のOSやアーキテクチャに特化したファイルで、現在のビルド環境では使用されないもの)を含んでいました。

問題は、IgnoredGoFilesに含まれるファイルがallgofilesにも含まれているにもかかわらず、vetコマンドへの引数として両方が結合されて渡されていた点にありました。これにより、ファイルが二重に処理される可能性がありました。さらに深刻な問題は、IgnoredGoFiles内のパスが完全修飾パス(絶対パス)ではなく、相対パスであったため、go vetがパッケージのルートディレクトリ以外から実行された場合に、これらのファイルを正しく見つけられないというものでした。結果として、go vetは存在しないファイルを処理しようとしてエラーを発生させていました。

このコミットは、これらの問題を解決し、go vetが常に正しいファイルセットを、正しいパスで処理するようにすることを目的としています。

前提知識の解説

  • go vet: Go言語の静的解析ツールの一つで、Goプログラムの潜在的なバグや疑わしい構成を検出します。例えば、フォーマット文字列の誤り、到達不能なコード、ロックの誤用などをチェックします。開発者がコードの品質を向上させ、一般的な間違いを避けるのに役立ちます。
  • pkg.allgofiles: Goのビルドシステムやツールが内部的に使用する構造体(go/buildパッケージのPackage型など)の一部で、特定のGoパッケージに属するすべてのGoソースファイルのリスト(ファイル名またはパスの配列)を指します。これには、通常のGoファイル、テストファイル、およびビルドタグによって条件付きで含まれる可能性のあるファイルが含まれます。
  • pkg.IgnoredGoFiles: 同様に、go/buildパッケージのPackage型の一部で、現在のビルドコンテキストでは無視されるべきGoソースファイルのリストを指します。これらは通常、ビルドタグ(例: // +build linux)によって特定のOSやアーキテクチャに限定されるファイルで、現在の環境ではコンパイルされないものです。
  • relPaths: この関数は、与えられたファイルパスのリストを、現在の作業ディレクトリからの相対パスに変換するユーティリティ関数であると推測されます。go vetのようなツールは、引数として相対パスを受け取ることが多いため、このような変換が必要になります。

技術的詳細

このコミットが修正している問題は、go vetコマンドが内部的にGoパッケージのファイルリストを処理する方法に起因していました。

  1. 二重処理の問題: src/cmd/go/vet.goの元のコードでは、runVet関数内でpkg.allgofilespkg.IgnoredGoFilesを結合してvetツールに渡していました。

    run(tool("vet"), relPaths(stringList(pkg.allgofiles, pkg.IgnoredGoFiles)))
    

    ここで、stringList関数は2つの文字列スライスを結合する役割を果たします。しかし、pkg.IgnoredGoFilesに含まれるファイルは、すでにpkg.allgofilesにも含まれている場合があります(例えば、allgofilesはパッケージディレクトリ内のすべてのGoファイルを列挙し、IgnoredGoFilesはそのサブセットであるため)。この冗長な結合により、vetツールが同じファイルを二度処理しようとする可能性がありました。これは非効率であるだけでなく、場合によっては予期せぬ動作を引き起こす可能性がありました。

  2. 不完全なパスの問題: さらに深刻なのは、pkg.IgnoredGoFiles内のファイルパスが完全修飾パス(絶対パス)ではなかったという点です。go vetがパッケージのルートディレクトリ以外の場所から実行された場合、相対パスであるIgnoredGoFiles内のファイルは、現在の作業ディレクトリからの相対パスとして解釈され、結果としてファイルが見つからないというエラーが発生していました。これは、go vetgoコマンドの一部として実行される際に、goコマンドが内部的にパッケージのコンテキストを正しく設定しない場合に顕在化する問題でした。

このコミットの修正は、これらの問題を直接的に解決します。vetコマンドに渡すファイルリストからpkg.IgnoredGoFilesを削除することで、二重処理の問題を解消します。pkg.allgofilesはすでにパッケージ内のすべての関連するGoファイルを完全なパスで含んでいるため、これだけで十分です。これにより、vetは常に正しいファイルセットを、正しい(見つけられる)パスで処理するようになります。

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

--- a/src/cmd/go/vet.go
+++ b/src/cmd/go/vet.go
@@ -32,6 +32,6 @@ func runVet(cmd *Command, args []string) {
 		// Use pkg.gofiles instead of pkg.Dir so that
 		// the command only applies to this package,
 		// not to packages in subdirectories.
-\t\trun(tool("vet"), relPaths(stringList(pkg.allgofiles, pkg.IgnoredGoFiles)))
+\t\trun(tool("vet"), relPaths(pkg.allgofiles))
 	}
 }

コアとなるコードの解説

変更はsrc/cmd/go/vet.goファイルのrunVet関数内の一行に集約されています。

元のコード:

run(tool("vet"), relPaths(stringList(pkg.allgofiles, pkg.IgnoredGoFiles)))

修正後のコード:

run(tool("vet"), relPaths(pkg.allgofiles))

この変更の核心は、vetツールに渡すファイルリストからpkg.IgnoredGoFilesを削除したことです。

  • tool("vet"): vetコマンドの実行可能パスを取得します。
  • relPaths(...): 取得したファイルパスのリストを、現在の作業ディレクトリからの相対パスに変換します。
  • stringList(pkg.allgofiles, pkg.IgnoredGoFiles) (変更前): pkg.allgofilespkg.IgnoredGoFilesの2つの文字列スライスを結合して、単一のリストを作成していました。
  • pkg.allgofiles (変更後): 修正後は、pkg.allgofilesのみをrelPaths関数に渡しています。

この修正により、go vetはパッケージ内のすべてのGoファイル(pkg.allgofilesに含まれるもの)のみを処理するようになります。pkg.IgnoredGoFilesに含まれるファイルは、ビルド時に無視されるべきものであり、vetによる静的解析の対象から外れるべきです。また、pkg.allgofilesは通常、完全修飾パスまたはgoコマンドが正しく解決できるパスでファイルを提供するため、パスの問題も同時に解決されます。

結果として、go vetはより正確なファイルセットに対して実行され、二重処理や「ファイルが見つからない」といったエラーが解消されます。

関連リンク

参考にした情報源リンク