[インデックス 14579] ファイルの概要
このコミットは、Go言語の実験的な型チェッカーである exp/gotype
パッケージに、より多くのテストを追加するものです。これにより、gotype
がGo標準ライブラリの大部分を処理できるようになることを目指しています。
コミット
commit e93bdd998c1c4e46dac4b21d2dca251e6016d3f2
Author: Robert Griesemer <gri@golang.org>
Date: Thu Dec 6 09:23:13 2012 -0800
exp/gotype: added many more tests
gotype can now handle much of the standard library.
- marked packages which have type checker issues
- this CL depends on CL 6846131
R=rsc
CC=golang-dev
https://golang.org/cl/6850130
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e93bdd998c1c4e46dac4b21d2dca251e6016d3f2
元コミット内容
exp/gotype: added many more tests
gotype
は現在、標準ライブラリの大部分を処理できます。
- 型チェッカーの問題があるパッケージをマークしました
- この変更リスト (CL) は CL 6846131 に依存します
変更の背景
Go言語の進化において、型システムは言語の健全性と堅牢性を保証する上で極めて重要な要素です。exp/gotype
は、Go言語の型チェックメカニズムを実験的に探求し、改善するためのパッケージでした。このコミットが行われた2012年12月時点では、Go言語はまだ比較的新しく、そのツールチェイン、特に型チェッカーは継続的に開発・改善されていました。
このコミットの主な背景は、exp/gotype
のカバレッジと堅牢性を向上させることにありました。具体的には、Go標準ライブラリの広範なパッケージに対して gotype
をテストすることで、その型チェック能力を検証し、既存の課題(例えば、特定の言語機能のサポート不足や、複雑な型構造の処理における問題)を特定することが目的でした。これにより、gotype
がより実用的な型チェッカーとして機能するための基盤を固めることが意図されています。
また、コミットメッセージにある「このCLはCL 6846131に依存します」という記述は、この変更が先行する別の変更(おそらく gotype
自体の機能改善やバグ修正)の上に成り立っていることを示唆しており、開発が段階的に進められていることを示しています。
前提知識の解説
Go言語の型システム
Go言語は静的型付け言語であり、プログラムのコンパイル時に型チェックが行われます。これにより、多くの一般的なプログラミングエラーを早期に発見し、実行時の安全性を高めることができます。Goの型システムは、構造体、インターフェース、ポインタ、スライス、マップなど、多様なデータ型をサポートしています。型チェックは、変数への値の代入、関数の引数と戻り値の型の一致、演算子の適用可能性などを検証します。
go/build
パッケージ
go/build
パッケージは、Goのソースコードパッケージを検索、解析、およびビルドするための情報を提供します。このパッケージは、Goのビルドシステムがどのように動作するかを理解し、Goのソースファイルやパッケージに関する情報をプログラム的に取得するために使用されます。例えば、特定のディレクトリにあるGoソースファイルからパッケージ情報を抽出し、そのパッケージが依存する他のパッケージを解決する能力を持っています。これは、Goのツール(コンパイラ、リンカ、goimports
、gofmt
など)が内部的に利用する重要なコンポーネントです。
exp/gotype
パッケージ
exp/gotype
は、Go言語の実験的な型チェッカーです。Goの標準ライブラリには、go/types
パッケージという公式の型チェッカーが存在しますが、exp/gotype
はその前身、あるいは異なるアプローチを試みるための実験的な実装であったと考えられます。型チェッカーの主な役割は、Goのソースコードを解析し、言語仕様に準拠した型付けが行われているかを検証することです。これには、変数の型推論、関数のシグネチャの一致、インターフェースの実装チェックなどが含まれます。実験的なパッケージであるため、当時の gotype
はまだ開発途上にあり、標準ライブラリのすべてのケースを完全に処理できるわけではありませんでした。
CL (Change List)
Go言語の開発では、変更は「Change List (CL)」としてGerrit(コードレビューシステム)に提出されます。各CLは一意の番号を持ち、関連する一連の変更を含みます。コミットメッセージに「CL 6846131に依存します」とあるのは、このコミットがその先行するCLによって導入された機能や修正を前提としていることを意味します。
技術的詳細
このコミットの技術的な核心は、exp/gotype
のテストハーネスを大幅に拡張し、Go標準ライブラリの広範なパッケージに対する型チェックの網羅性を高めた点にあります。
runTest
関数の変更
以前の runTest
関数は、path
と pkg
(パッケージ名)を直接引数として受け取っていました。しかし、変更後には path
のみを受け取るように簡略化されました。この変更の背後には、テスト対象が単一のGoファイルであるか、あるいはパッケージディレクトリ全体であるかを runTest
内部で動的に判断し、適切な処理を行うという設計思想があります。
- 単一ファイルの場合:
path
が.go
で終わる場合、それは単一のGoファイルとして扱われます。filepath.Join(runtime.GOROOT(), "src/pkg", path)
を用いてファイルの絶対パスを構築し、filepath.Split
でディレクトリとファイル名を分離します。パッケージ名はファイル名から.go
拡張子を除いたものとして推測されます。その後、processFiles
関数が呼び出され、指定されたファイルに対して型チェックが実行されます。 - パッケージディレクトリの場合:
path
が.go
で終わらない場合、それはパッケージディレクトリとして扱われます。ここで重要なのは、go/build.Import
関数が導入されたことです。build.Import(path, "", 0)
を呼び出すことで、指定されたパスのGoパッケージに関する詳細な情報(パッケージ名、ディレクトリ、含まれるGoファイルのリストなど)を取得します。これにより、gotype
はGoのビルドシステムがパッケージをどのように解決するかを模倣し、より正確なパッケージの型チェックが可能になります。取得したパッケージ情報から、pkg.GoFiles
を用いてパッケージ内のすべてのGoファイルの絶対パスを構築し、processFiles
に渡して型チェックを実行します。
この go/build
パッケージの利用は、gotype
がGoの公式なパッケージ解決ロジックに準拠し、より現実的なシナリオでの型チェックを可能にするための重要なステップです。TODO(gri) gotype should use the build package instead
というコメントは、このテストの変更が、将来的に gotype
自体が go/build
パッケージを直接利用するようにリファクタリングされることを見越したものであることを示唆しています。
テスト対象パッケージの拡張
tests
変数は、以前は少数の go/
パッケージ(go/ast
, go/build
など)のみを対象としていましたが、このコミットにより、Go標準ライブラリの非常に多くのパッケージがテスト対象として追加されました。これには、archive/zip
, bufio
, bytes
, compress/*
, crypto/*
, encoding/*
, fmt
, image/*
, io
, log
, math
, net/http
, regexp
, runtime
, sort
, strings
, testing
, text/*
, unicode/*
など、多岐にわたるパッケージが含まれます。
注目すべきは、追加されたパッケージの多くがコメントアウトされており、その理由が具体的に記されている点です。例えば:
// "archive/tar", // investigate
(調査が必要)// "encoding/binary", // complex() doesn't work yet
(complex
型の処理がまだ不完全)// "go/doc", // variadic parameters don't work yet fully
(可変長引数の処理がまだ不完全)// "net", // depends on C files
(C言語のファイルに依存しているため、Goの型チェッカーだけでは完結しない)// "sync", // platform-specific files
(プラットフォーム固有のファイルが含まれるため、一般的なテストが難しい)
これらのコメントは、当時の exp/gotype
が直面していた具体的な技術的課題を浮き彫りにしています。型チェッカーがGo言語のすべての機能を完全にサポートするためには、complex
型、可変長引数、Cgo(C言語との連携)、プラットフォーム固有のコードなど、様々な複雑なケースを正確に処理する必要があることが示されています。
testdata/test1.go
の変更
testdata/test1.go
のパッケージ名が package p
から package test1
に変更されました。これは、テストデータの識別をより明確にするための軽微な変更であり、テストの構造や命名規則の改善の一環と考えられます。
コアとなるコードの変更箇所
src/pkg/exp/gotype/gotype_test.go
--- a/src/pkg/exp/gotype/gotype_test.go
+++ b/src/pkg/exp/gotype/gotype_test.go
@@ -5,20 +5,38 @@
package main
import (
+ "go/build"
"path/filepath"
"runtime"
+ "strings"
"testing"
)
-func runTest(t *testing.T, path, pkg string) {
+func runTest(t *testing.T, path string) {
exitCode = 0
- *pkgName = pkg
- *recursive = false
+
+ *recursive = false
+ if suffix := ".go"; strings.HasSuffix(path, suffix) {
+ // single file
+ path = filepath.Join(runtime.GOROOT(), "src/pkg", path)
+ path, file := filepath.Split(path)
+ *pkgName = file[:len(file)-len(suffix)]
+ processFiles([]string{path}, true)
+ } else {
+ // package directory
+ // TODO(gri) gotype should use the build package instead
+ pkg, err := build.Import(path, "", 0)
+ if err != nil {
+ t.Errorf("build.Import error for path = %s: %s", path, err)
+ return
+ }
+ // TODO(gri) there ought to be a more direct way using the build package...
+ files := make([]string, len(pkg.GoFiles))
+ for i, file := range pkg.GoFiles {
+ files[i] = filepath.Join(pkg.Dir, file)
+ }
+ *pkgName = pkg.Name
+ processFiles(files, true)
+ }
- if pkg == "" {
- processFiles([]string{path}, true)
- } else {
- processDirectory(path)
- }
-
if exitCode != 0 {
@@ -26,26 +44,167 @@ func runTest(t *testing.T, path, pkg string) {
}
}
-var tests = []struct {
- path string
- pkg string
-}{
+var tests = []string{
// individual files
- {"testdata/test1.go", ""},
+ "exp/gotype/testdata/test1.go",
// directories
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/build"), "build"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/printer"), "printer"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "token"},
- {filepath.Join(runtime.GOROOT(), "src/pkg/exp/types"), "types"},
+ // Note: packages that don't typecheck yet are commented out
+ // "archive/tar", // investigate
+ "archive/zip",
+
+ "bufio",
+ "bytes",
+
+ "compress/bzip2",
+ "compress/flate",
+ "compress/gzip",
+ "compress/lzw",
+ "compress/zlib",
+
+ "container/heap",
+ "container/list",
+ "container/ring",
+
+ "crypto",
+ "crypto/aes",
+ "crypto/cipher",
+ "crypto/des",
+ "crypto/dsa",
+ "crypto/ecdsa",
+ "crypto/elliptic",
+ "crypto/hmac",
+ "crypto/md5",
+ "crypto/rand",
+ "crypto/rc4",
+ "crypto/rsa",
+ "crypto/sha1",
+ "crypto/sha256",
+ "crypto/sha512",
+ "crypto/subtle",
+ "crypto/tls",
+ // "crypto/x509", // investigate
+ "crypto/x509/pkix",
+
+ "database/sql",
+ "database/sql/driver",
+
+ "debug/dwarf",
+ "debug/elf",
+ "debug/gosym",
+ "debug/macho",
+ "debug/pe",
+
+ "encoding/ascii85",
+ "encoding/asn1",
+ "encoding/base32",
+ "encoding/base64",
+ // "encoding/binary", // complex() doesn't work yet
+ "encoding/csv",
+ // "encoding/gob", // complex() doesn't work yet
+ "encoding/hex",
+ "encoding/json",
+ "encoding/pem",
+ "encoding/xml",
+
+ "errors",
+ "expvar",
+ "flag",
+ "fmt",
+
+ "exp/types",
+ "exp/gotype",
+
+ "go/ast",
+ "go/build",
+ // "go/doc", // variadic parameters don't work yet fully
+ "go/format",
+ "go/parser",
+ "go/printer",
+ "go/scanner",
+ "go/token",
+
+ "hash/adler32",
+ // "hash/crc32", // investigate
+ "hash/crc64",
+ "hash/fnv",
+
+ "image",
+ "image/color",
+ "image/draw",
+ "image/gif",
+ "image/jpeg",
+ "image/png",
+
+ "index/suffixarray",
+
+ "io",
+ // "io/ioutil", // investigate
+
+ "log",
+ "log/syslog",
+
+ "math",
+ // "math/big", // investigate
+ // "math/cmplx", // complex doesn't work yet
+ "math/rand",
+
+ "mime",
+ "mime/multipart",
+
+ // "net", // depends on C files
+ "net/http",
+ "net/http/cgi",
+ // "net/http/fcgi", // investigate
+ "net/http/httptest",
+ "net/http/httputil",
+ // "net/http/pprof", // investigate
+ "net/mail",
+ // "net/rpc", // investigate
+ "net/rpc/jsonrpc",
+ "net/smtp",
+ "net/textproto",
+ "net/url",
+
+ // "path", // variadic parameters don't work yet fully
+ // "path/filepath", // investigate
+
+ // "reflect", // investigate
+
+ "regexp",
+ "regexp/syntax",
+
+ "runtime",
+ // "runtime/cgo", // import "C"
+ "runtime/debug",
+ "runtime/pprof",
+
+ "sort",
+ // "strconv", // investigate
+ "strings",
+
+ // "sync", // platform-specific files
+ // "sync/atomic", // platform-specific files
+
+ // "syscall", // platform-specific files
+
+ "testing",
+ "testing/iotest",
+ "testing/quick",
+
+ "text/scanner",
+ "text/tabwriter",
+ // "text/template", // variadic parameters don't work yet fully
+ // "text/template/parse", // variadic parameters don't work yet fully
+
+ // "time", // platform-specific files
+ "unicode",
+ "unicode/utf16",
+ "unicode/utf8",
+}
+
+func Test(t *testing.T) {
for _, test := range tests {
- runTest(t, test.path, test.pkg)
+ runTest(t, test)
}
}
src/pkg/exp/gotype/testdata/test1.go
--- a/src/pkg/exp/gotype/testdata/test1.go
+++ b/src/pkg/exp/gotype/testdata/test1.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package p
+package test1
func _() {
// the scope of a local type declaration starts immediately after the type name
コアとなるコードの解説
gotype_test.go
の変更点
-
runTest
関数のシグネチャ変更と内部ロジックの強化:- 変更前:
func runTest(t *testing.T, path, pkg string)
- 変更後:
func runTest(t *testing.T, path string)
- この変更により、
runTest
はテスト対象のパス(ファイルまたはディレクトリ)のみを受け取るようになりました。 - 内部では、
strings.HasSuffix(path, suffix)
を使用して、パスが.go
で終わるかどうかをチェックし、単一のGoファイルかパッケージディレクトリかを判別します。 - 単一ファイルの場合:
filepath.Join(runtime.GOROOT(), "src/pkg", path)
でGoのソースツリー内の絶対パスを構築し、filepath.Split
でファイル名とディレクトリを分離します。*pkgName
グローバル変数にファイル名から拡張子を除いたものを設定し、processFiles
関数を呼び出して型チェックを実行します。 - パッケージディレクトリの場合:
go/build
パッケージのbuild.Import
関数を導入しました。これは、Goのビルドシステムがパッケージをどのように解決するかを模倣し、指定されたパスのパッケージに関する情報(パッケージ名、ディレクトリ、含まれるGoファイルのリストなど)を取得するために使用されます。これにより、gotype
はより正確なパッケージの型チェックが可能になります。取得したpkg.GoFiles
をループして、各Goファイルの絶対パスを構築し、processFiles
に渡して型チェックを実行します。 TODO(gri) gotype should use the build package instead
というコメントは、このテストの変更が、将来的にgotype
自体がgo/build
パッケージを直接利用するようにリファクタリングされることを見越したものであることを示唆しています。
- 変更前:
-
tests
変数の構造変更とテスト対象の拡張:- 変更前:
var tests = []struct { path string; pkg string }
- 変更後:
var tests = []string
runTest
関数のシグネチャ変更に合わせて、tests
スライスは単にテスト対象のパッケージパス(またはファイルパス)の文字列のリストになりました。- 最も顕著な変更は、Go標準ライブラリの非常に多くのパッケージがこのリストに追加されたことです。これにより、
exp/gotype
の型チェック能力が広範なコードベースに対して検証されるようになりました。 - コメントアウトされたパッケージとその理由(例:
complex()
がまだ動作しない、可変長引数が不完全、Cファイルに依存、プラットフォーム固有のファイルなど)は、当時のgotype
が直面していた具体的な課題を示しています。
- 変更前:
testdata/test1.go
の変更点
- パッケージ名が
package p
からpackage test1
に変更されました。これは、テストデータの識別をより明確にするための軽微な変更です。
これらの変更は、exp/gotype
がGo標準ライブラリの大部分を型チェックできるようになるための重要なステップであり、型チェッカーの堅牢性とカバレッジを向上させることを目的としています。特に go/build
パッケージの利用は、Goの公式なパッケージ解決ロジックとの整合性を高める上で重要です。
関連リンク
- Go言語公式ドキュメント: https://go.dev/doc/
go/build
パッケージドキュメント: https://pkg.go.dev/go/buildgo/types
パッケージドキュメント (現在の公式型チェッカー): https://pkg.go.dev/go/types
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go
- Gerrit Code Review (Goプロジェクト): https://go-review.googlesource.com/
- CL 6850130 (このコミットのGerritページ): https://golang.org/cl/6850130
- CL 6846131 (このコミットが依存するCL): https://golang.org/cl/6846131 (Web検索により、
exp/gotype: fix type checking of composite literals
という内容であることが確認できます。これは、複合リテラルの型チェックに関する修正であり、今回のテスト追加の前提となるgotype
の機能改善です。)