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

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

このコミットは、Go言語の標準ライブラリの一部である2つのファイル、src/pkg/exp/norm/maketables.gosrc/pkg/text/template/parse/lex.go に対する修正を含んでいます。これらの修正は、Go言語の静的解析ツールである go vet によって検出されたエラーを解消することを目的としています。

  • src/pkg/exp/norm/maketables.go: このファイルは、Unicode正規化に関連するテーブルを生成するための実験的なパッケージの一部です。具体的には、文字の正規化プロパティ、特にCanonical Combining Class (CCC) の検証ロジックが含まれています。
  • src/pkg/text/template/parse/lex.go: このファイルは、Goの標準テンプレートパッケージ text/template の字句解析(lexing)部分を担当しています。テンプレート文字列をトークンに分割する処理を行います。

コミット

  • コミットハッシュ: 459837c86ecf583ea7597b47b1d2591c332440c9
  • 作者: Rob Pike r@golang.org
  • 日付: 2012年4月25日 (水) 11:33:27 +1000
  • 変更ファイル数: 2
  • 変更行数: 3行の追加、3行の削除

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

https://github.com/golang/go/commit/459837c86ecf583ea7597b47b1d2591c332440c9

元コミット内容

    all: fix errors found by go vet

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

変更の背景

このコミットの主な背景は、Go言語の公式静的解析ツールである go vet によって検出されたエラーを修正することです。go vet は、Goプログラムの潜在的なバグや疑わしい構造を特定するために設計されており、コンパイルは通るものの、実行時に問題を引き起こす可能性のあるコードパターンを警告します。

具体的には、このコミットで修正されたエラーは、fmt パッケージのフォーマット文字列における引数の型とフォーマット動詞の不一致、およびより詳細なデバッグ情報の提供に関するものです。go vet は、fmt.Printflogger.Fatalf のような関数呼び出しにおいて、フォーマット動詞(例: %U, %d)が対応する引数の型と一致しない場合に警告を発します。これにより、実行時に予期せぬ出力やパニックが発生するのを防ぎます。

このコミットは、コードの堅牢性とデバッグのしやすさを向上させるための、一般的なコード品質改善の一環として行われました。

前提知識の解説

go vet ツール

go vet は、Go言語のソースコードを静的に分析し、潜在的なエラーや疑わしい構造を報告するコマンドラインツールです。コンパイラが検出できないような、しかし実行時に問題を引き起こす可能性のあるバグパターン(例: Printf フォーマット文字列の誤用、到達不能なコード、ロックの誤用など)を特定します。go vet はGoのツールチェインの一部であり、Go開発者がコードの品質と信頼性を維持するために広く利用されています。

Go言語の fmt パッケージとフォーマット動詞

Go言語の fmt パッケージは、フォーマットされたI/O(入出力)を実装するための機能を提供します。fmt.Printffmt.Errorflog.Fatalf など、多くの関数がC言語の printf に似たフォーマット文字列を使用します。フォーマット文字列には、引数をどのように表示するかを指示する「フォーマット動詞」が含まれます。

このコミットに関連する主なフォーマット動詞は以下の通りです。

  • %U: Unicodeコードポイントを U+HHHH 形式で表示します。引数は rune 型(または int 型でUnicodeコードポイントを表すもの)を期待します。
  • %+U: %U と同じですが、常に U+HHHH 形式で表示します。
  • %#U: Unicodeコードポイントを U+HHHH 'c' 形式で表示します。HHHH はコードポイントの16進数表現、'c' はそのコードポイントに対応する文字です。これはデバッグ時に非常に有用で、コードポイントだけでなく、それがどの文字を表しているのかも一目で確認できます。引数は rune 型を期待します。
  • %d: 整数を10進数で表示します。引数は整数型を期待します。

go vet は、例えば %U が指定されているにもかかわらず、rune 型ではない int 型の引数が渡された場合などに警告を発します。これは、%U が期待する形式で引数を解釈できないため、予期せぬ出力やエラーにつながる可能性があるためです。

Unicode正規化とCanonical Combining Class (CCC)

Unicode正規化は、異なるバイト列で表現されながらも同じ意味を持つ文字シーケンス(例: アクセント付き文字)を、一貫した単一の表現に変換するプロセスです。これにより、テキストの比較や検索が正確に行えるようになります。

Canonical Combining Class (CCC) は、Unicode文字のプロパティの一つで、結合文字(例: アクセント記号)が正規化中にどのように並べ替えられるかを決定します。CCCの値は0から255までの範囲で、0は結合文字ではない文字(基底文字)を示し、0以外の値は結合文字の結合順序を示します。src/pkg/exp/norm/maketables.go は、このCCCを含むUnicodeのプロパティテーブルを生成するロジックを含んでおり、その検証中に go vet のエラーが検出されました。

Goのテンプレートエンジンと字句解析

Goの text/template パッケージは、データ構造をテキスト出力に変換するためのテンプレートエンジンを提供します。このエンジンは、テンプレート文字列を解析し、データと結合して最終的な出力を生成します。

字句解析(lexing)は、コンパイラやインタプリタの最初のフェーズであり、入力文字列(この場合はテンプレート文字列)を意味のある最小単位である「トークン」のシーケンスに分解するプロセスです。src/pkg/text/template/parse/lex.go は、この字句解析器の実装を含んでおり、テンプレート内の文字を読み込み、エラーが発生した場合に報告する役割を担っています。

技術的詳細

このコミットは、go vet が検出した2種類の問題を修正しています。

  1. fmt.Printf フォーマット動詞 %U の引数不一致の修正 (src/pkg/exp/norm/maketables.go): src/pkg/exp/norm/maketables.goprintCharInfoTables 関数内で、logger.Fatalf の呼び出しにおいて %U フォーマット動詞が使用されていました。しかし、この %U に渡されていた引数は cc (Canonical Combining Class) という int 型の変数でした。%U はUnicodeコードポイント(rune 型または int 型でコードポイントを表すもの)を期待しますが、cc は文字のプロパティ値であり、直接コードポイントを表すものではありません。 go vet はこの不一致を検出し、警告を発しました。修正では、cc の代わりに、ループの現在の文字を表す r (rune) を %U の引数として渡すように変更されました。これにより、logger.Fatalf の出力が、エラーが発生した文字のUnicodeコードポイントを正しく表示するようになり、デバッグ情報がより正確になりました。

    • 変更前: logger.Fatalf("%U: ...", cc)
    • 変更後: logger.Fatalf("%U: ...", r, cc)
  2. fmt.Errorf フォーマット動詞 %+U から %#U への変更 (src/pkg/text/template/parse/lex.go): src/pkg/text/template/parse/lex.golexText 関数内で、l.errorf の呼び出しにおいて bad character %+U というフォーマット文字列が使用されていました。%+UU+HHHH 形式でUnicodeコードポイントを表示しますが、デバッグ時にはそのコードポイントが実際にどの文字を表しているのかも知りたい場合があります。 go vet は、%+U の代わりに %#U を使用することを推奨する場合があります。%#UU+HHHH 'c' 形式で、コードポイントとその文字表現の両方を出力します。これにより、エラーメッセージがより詳細になり、問題の文字を特定しやすくなります。この変更は、デバッグ情報の質を向上させるためのものです。

    • 変更前: return l.errorf("bad character %+U", r)
    • 変更後: return l.errorf("bad character %#U", r)

これらの変更は、Goのコードベース全体で go vet の警告を解消し、コードの品質とデバッグの容易性を向上させるための、一般的なメンテナンス作業の一環です。

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

src/pkg/exp/norm/maketables.go

--- a/src/pkg/exp/norm/maketables.go
+++ b/src/pkg/exp/norm/maketables.go
@@ -607,10 +607,10 @@ func printCharInfoTables() int {
 		tccc := ccc(d[len(d)-1])
 		cc := ccc(r)
 		if cc != 0 && lccc == 0 && tccc == 0 {
-			logger.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", cc)
+			logger.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", r, cc)
 		}
 		if tccc < lccc && lccc != 0 {
 			const msg = "%U: lccc (%d) must be <= tcc (%d)"
 			logger.Fatalf(msg, r, lccc, tccc)
 		}
 		if cc != lccc {
 			if cc != 0 {
-				logger.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", cc)
+				logger.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", r, cc)
 			}
 			index = 3
 		}

src/pkg/text/template/parse/lex.go

--- a/src/pkg/text/template/parse/lex.go
+++ b/src/pkg/text/template/parse/lex.go
@@ -348,7 +348,7 @@ Loop:
 			l.backup()
 			word := l.input[l.start:l.pos]
 			if !l.atTerminator() {
-				return l.errorf("bad character %+U", r)
+				return l.errorf("bad character %#U", r)
 			}
 			switch {
 			case key[word] > itemKeyword:

コアとなるコードの解説

src/pkg/exp/norm/maketables.go の変更

// 変更前
logger.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", cc)
// 変更後
logger.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", r, cc)

この変更は、logger.Fatalf の呼び出しにおけるフォーマット文字列の引数に関するものです。

  • 変更前は、%U フォーマット動詞に対して cc (Canonical Combining Class) という int 型の変数が渡されていました。cc は文字の結合クラスを表す数値であり、Unicodeコードポイントそのものではありません。go vet は、%U が期待する rune (Unicodeコードポイント) と cc の型が一致しない、または意図しない使い方であると判断し、警告を発しました。
  • 変更後は、%U の引数として r が追加されました。r はループの現在のイテレーションで処理されている rune (Unicodeコードポイント) です。これにより、%U は正しく文字のコードポイントを表示し、%dcc の値を表示するようになります。結果として、エラーメッセージが「どの文字で、その文字のCCCが何であるか」を正確に伝えるようになり、デバッグが容易になります。

同様の修正が、少し下の行でも行われています。

// 変更前
logger.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", cc)
// 変更後
logger.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", r, cc)

これも上記と同様に、%Ur を渡し、cc%d に対応させることで、go vet の警告を解消し、より正確なエラーメッセージを出力するように修正されています。

src/pkg/text/template/parse/lex.go の変更

// 変更前
return l.errorf("bad character %+U", r)
// 変更後
return l.errorf("bad character %#U", r)

この変更は、l.errorf の呼び出しにおけるフォーマット動詞の変更です。

  • 変更前は %+U が使用されていました。これはUnicodeコードポイントを U+HHHH の形式で表示します。
  • 変更後は %#U に変更されました。これはUnicodeコードポイントを U+HHHH 'c' の形式で表示します。ここで 'c' はそのコードポイントに対応する実際の文字です。
  • この変更により、エラーメッセージに文字そのものが含まれるようになり、デバッグ時にどの文字が問題を引き起こしたのかを視覚的に、かつより明確に把握できるようになります。これは go vet が推奨する、より情報量の多いフォーマット動詞の使用例です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (上記「関連リンク」に記載の各パッケージドキュメント)
  • go vet の使用例や解説を含むGoコミュニティの議論やブログ記事 (具体的なURLは特定していませんが、一般的な知識として参照しました)
  • UnicodeのCanonical Combining Class (CCC) に関する一般的な情報 (Unicode標準のドキュメントなど)
  • Go言語の fmt パッケージのフォーマット動詞に関する情報 (特に %U, %+U, %#U の違い)

これらの情報源は、go vet の機能、fmt パッケージのフォーマット動詞の挙動、および関連するGo言語の標準ライブラリのコンテキストを理解するために参照されました。