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

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

このコミットは、Go言語の標準ライブラリであるfmtパッケージにおけるエラー出力の書式を変更するものです。具体的には、Printf系の関数が生成するエラーメッセージに感嘆符 ! を追加することで、エラーがより明確に識別できるように改善されています。

コミット

commit 02ad82fe153ee64cde7823a4ca2fb594624bb82e
Author: Rob Pike <r@golang.org>
Date:   Wed Jul 31 14:08:47 2013 +1000

    fmt: put a ! in all error output generated by Printf-like functions
    Fixes #5730.
    
    R=golang-dev, dsymonds
    CC=golang-dev
    https://golang.org/cl/12141043

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

https://github.com/golang/go/commit/02ad82fe153ee64cde7823a4ca2fb594624bb82e

元コミット内容

fmt: put a ! in all error output generated by Printf-like functions Fixes #5730.

このコミットは、Printfのような書式設定関数によって生成される全てのエラー出力に ! を追加することを目的としています。これは、Issue #5730を修正するものです。

変更の背景

この変更の背景には、Go言語のfmtパッケージが生成するエラーメッセージの視認性と一貫性の向上が挙げられます。fmtパッケージは、Goプログラムにおいて文字列の書式設定と出力を行うための中心的な役割を担っています。しかし、書式指定子が不正であったり、引数の型が不適切であったり、インデックスが範囲外であったりする場合など、様々な理由でエラーが発生することがあります。

以前のfmtパッケージのエラー出力は、例えば%!(BADINDEX)のように、エラーの種類を示す文字列が括弧で囲まれていました。しかし、これらのエラーメッセージは通常の出力と混同される可能性があり、特に複雑な書式設定文字列を使用している場合や、デバッグ時にエラーの発生源を特定する際に、エラーであることの認識が遅れることがありました。

Issue #5730("fmt: make all error messages start with %!")では、この問題が提起され、全てのエラーメッセージを%!で始めることで、エラー出力の一貫性を高め、ユーザーがエラーを即座に識別できるようにすべきだという提案がなされました。このコミットは、その提案を受けて、既存の%!プレフィックスに加えて、エラーメッセージの本体にも!を追加することで、エラーの強調をさらに強化しています。これにより、エラーメッセージが通常の出力と明確に区別され、デバッグ作業の効率化に貢献します。

前提知識の解説

このコミットを理解するためには、以下のGo言語のfmtパッケージに関する知識が必要です。

  • fmtパッケージ: Go言語の標準ライブラリの一部で、書式設定されたI/O(入力/出力)を実装します。Printf, Sprintf, Errorfなどの関数を提供し、様々なデータ型を文字列に変換したり、指定された書式で出力したりする機能を持っています。
  • 書式指定子 (Format Verbs): Printf系の関数で使用される特殊なプレースホルダーで、引数の値をどのように文字列に変換するかを指示します。例えば、%dは整数、%sは文字列、%vはGoのデフォルト書式、%#vはGoの構文で表現可能な書式を表します。
  • 引数インデックス (Argument Index): 書式指定子内で[n]のように指定することで、引数のリストからn番目の引数を使用するように指示できます。例えば、%[2]dは2番目の引数を整数として書式設定します。
  • エラー出力の慣習: fmtパッケージは、書式設定エラーが発生した場合に、特定の形式でエラーメッセージを生成します。歴史的に、これらのエラーメッセージは%!で始まり、その後にエラーの種類を示す情報が続くことが一般的でした。このコミットは、その慣習をさらに強化するものです。
  • io.ErrUnexpectedEOF: ioパッケージで定義されているエラーで、予期せぬファイルの終端に達したことを示します。このコミットのテストコードで、パニック時のエラーメッセージのテストケースとして使用されています。
  • パニック (Panic): Go言語におけるランタイムエラーの一種で、プログラムの実行を中断させます。fmtパッケージは、書式設定中にパニックが発生した場合にも、その情報をエラーメッセージとして出力することがあります。

技術的詳細

このコミットの技術的な変更は、fmtパッケージ内部でエラーメッセージを生成する際に使用されるバイトスライス([]byte)の定義を変更することによって実現されています。

具体的には、src/pkg/fmt/print.goファイル内で定義されている以下のバイトスライスが変更されています。

  • missingBytes: 書式指定子に対応する引数が不足している場合のエラーメッセージ。
    • 変更前: (MISSING)
    • 変更後: !(MISSING)
  • badIndexBytes: 引数インデックスが不正な場合のエラーメッセージ。
    • 変更前: (BADINDEX)
    • 変更後: !(BADINDEX)
  • panicBytes: 書式設定中にパニックが発生した場合のエラーメッセージ。
    • 変更前: (PANIC=
    • 変更後: !(PANIC=

これらの変更により、fmtパッケージがこれらのエラーを検出してメッセージを生成する際に、自動的に!が追加されるようになります。

また、src/pkg/fmt/doc.goのドキュメントとsrc/pkg/fmt/fmt_test.goのテストケースも、この新しいエラー出力形式に合わせて更新されています。これにより、ドキュメントは最新の挙動を反映し、テストは変更が正しく適用され、期待通りのエラーメッセージが生成されることを保証します。

この変更は、fmtパッケージの内部実装に深く関わるものであり、外部からfmtパッケージを利用するユーザーが直接これらのバイトスライスを操作することはありません。しかし、この変更によって生成されるエラーメッセージの形式は、Goプログラムのデバッグやエラーハンドリングにおいて重要な影響を与えます。

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

このコミットにおける主要なコード変更は、以下の3つのファイルにわたっています。

  1. src/pkg/fmt/doc.go: fmtパッケージのドキュメントファイル。エラー出力の例が更新されています。

    --- a/src/pkg/fmt/doc.go
    +++ b/src/pkg/fmt/doc.go
    @@ -156,8 +156,8 @@
     			Printf("%*s", 4.5, "hi"):  %!(BADWIDTH)hi
     			Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi
     		Invalid or invalid use of argument index: %!(BADINDEX)
    -			Printf("%*[2]d", 7):       %d(BADINDEX)
    -			Printf("%.[2]d", 7):       %d(BADINDEX)
    +			Printf("%*[2]d", 7):       %d!(BADINDEX)
    +			Printf("%.[2]d", 7):       %d!(BADINDEX)
     
     	All errors begin with the string "%!" followed sometimes
     	by a single character (the verb) and end with a parenthesized
    
  2. src/pkg/fmt/fmt_test.go: fmtパッケージのテストファイル。エラーケースの期待される出力が更新されています。

    --- a/src/pkg/fmt/fmt_test.go
    +++ b/src/pkg/fmt/fmt_test.go
    @@ -563,17 +563,17 @@ var reorderTests = []struct {
     	{"%d %d %d %#[1]o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015"},
     
     	// Erroneous cases.
    -	{"%[d", SE{2, 1}, "%d(BADINDEX)"},
    +	{"%[d", SE{2, 1}, "%d!(BADINDEX)"},
     	{"%]d", SE{2, 1}, "%!](int=2)d%!(EXTRA int=1)"},
    -	{"%[]d", SE{2, 1}, "%d(BADINDEX)"},
    -	{"%[-3]d", SE{2, 1}, "%d(BADINDEX)"},
    -	{"%[99]d", SE{2, 1}, "%d(BADINDEX)"},
    +	{"%[]d", SE{2, 1}, "%d!(BADINDEX)"},
    +	{"%[-3]d", SE{2, 1}, "%d!(BADINDEX)"},
    +	{"%[99]d", SE{2, 1}, "%d!(BADINDEX)"},
     	{"%[3]", SE{2, 1}, "%!(NOVERB)"},
    -	{"%[1].2d", SE{5, 6}, "%d(BADINDEX)"},
    -	{"%[1]2d", SE{2, 1}, "%d(BADINDEX)"},
    -	{"%3.[2]d", SE{7}, "%d(BADINDEX)"},
    -	{"%.[2]d", SE{7}, "%d(BADINDEX)"},
    -	{"%d %d %d %#[1]o %#o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015 %o(MISSING)"},
    +	{"%[1].2d", SE{5, 6}, "%d!(BADINDEX)"},
    +	{"%[1]2d", SE{2, 1}, "%d!(BADINDEX)"},
    +	{"%3.[2]d", SE{7}, "%d!(BADINDEX)"},
    +	{"%.[2]d", SE{7}, "%d!(BADINDEX)"},
    +	{"%d %d %d %#[1]o %#o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015 %o!(MISSING)"},
     }
     
     func TestReorder(t *testing.T) {
    @@ -877,16 +877,16 @@ var panictests = []struct {
     }{
     	// String
     	{"%s", (*Panic)(nil), "<nil>"}, // nil pointer special case
    -	{"%s", Panic{io.ErrUnexpectedEOF}, "%s(PANIC=unexpected EOF)"},
    -	{"%s", Panic{3}, "%s(PANIC=3)"},
    +	{"%s", Panic{io.ErrUnexpectedEOF}, "%s!(PANIC=unexpected EOF)"},
    +	{"%s", Panic{3}, "%s!(PANIC=3)"},
     	// GoString
     	{"%#v", (*Panic)(nil), "<nil>"}, // nil pointer special case
    -	{"%#v", Panic{io.ErrUnexpectedEOF}, "%v(PANIC=unexpected EOF)"},
    -	{"%#v", Panic{3}, "%v(PANIC=3)"},
    +	{"%#v", Panic{io.ErrUnexpectedEOF}, "%v!(PANIC=unexpected EOF)"},
    +	{"%#v", Panic{3}, "%v!(PANIC=3)"},
     	// Format
     	{"%s", (*PanicF)(nil), "<nil>"}, // nil pointer special case
    -	{"%s", PanicF{io.ErrUnexpectedEOF}, "%s(PANIC=unexpected EOF)"},
    -	{"%s", PanicF{3}, "%s(PANIC=3)"},
    +	{"%s", PanicF{io.ErrUnexpectedEOF}, "%s!(PANIC=unexpected EOF)"},
    +	{"%s", PanicF{3}, "%s!(PANIC=3)"},
     }
     
     func TestPanics(t *testing.T) {
    
    
  3. src/pkg/fmt/print.go: fmtパッケージの内部実装ファイル。エラーメッセージのバイトスライス定義が変更されています。

    --- a/src/pkg/fmt/print.go
    +++ b/src/pkg/fmt/print.go
    @@ -21,9 +21,9 @@ var (
     	nilParenBytes   = []byte("(nil)")
     	nilBytes        = []byte("nil")
     	mapBytes        = []byte("map[")
    -	missingBytes    = []byte("(MISSING)")
    -	badIndexBytes   = []byte("(BADINDEX)")
    -	panicBytes      = []byte("(PANIC=")
    +	missingBytes    = []byte("!(MISSING)")
    +	badIndexBytes   = []byte("!(BADINDEX)")
    +	panicBytes      = []byte("!(PANIC=")
     	extraBytes      = []byte("%!(EXTRA ")
     	irparenBytes    = []byte("i)")
     	bytesBytes      = []byte("[]byte{")
    

コアとなるコードの解説

このコミットの核心は、src/pkg/fmt/print.goファイル内の以下の3つのバイトスライスの定義変更にあります。

  • missingBytes:

    • 変更前: []byte("(MISSING)")
    • 変更後: []byte("!(MISSING)")
    • このバイトスライスは、Printf系の関数が書式指定子に対応する引数を見つけられなかった場合(例えば、%dがあるのに整数型の引数が渡されなかった場合)に、エラーメッセージの一部として使用されます。変更により、(の前に!が追加され、エラーの視認性が向上します。
  • badIndexBytes:

    • 変更前: []byte("(BADINDEX)")
    • 変更後: []byte("!(BADINDEX)")
    • このバイトスライスは、書式指定子内で指定された引数インデックス(例: %[2]d)が不正な場合(例えば、存在しないインデックスを指定した場合)に、エラーメッセージの一部として使用されます。同様に、(の前に!が追加されます。
  • panicBytes:

    • 変更前: []byte("(PANIC=")
    • 変更後: []byte("!(PANIC=")
    • このバイトスライスは、fmtパッケージが書式設定処理中にパニックを捕捉した場合に、そのパニック情報をエラーメッセージに含める際に使用されます。変更により、(の前に!が追加され、パニックによるエラーがより明確に示されます。

これらのバイトスライスは、fmtパッケージの内部でエラーメッセージを構築する際に参照されます。例えば、fmtパッケージの書式設定ロジックは、引数不足を検出するとmissingBytesの内容をエラー出力に含めます。この変更により、これらのエラーメッセージは自動的に!を含むようになり、ユーザーは出力を見ただけでそれがfmtパッケージによって生成されたエラーであることを即座に認識できるようになります。

src/pkg/fmt/doc.goの変更は、この新しいエラー出力形式を公式ドキュメントに反映させるためのものです。これにより、ユーザーはfmtパッケージの挙動について正確な情報を得ることができます。

src/pkg/fmt/fmt_test.goの変更は、この新しいエラー出力形式が期待通りに機能することを検証するためのテストケースの更新です。既存のテストケースの期待値が、!が追加された新しいエラーメッセージに修正されています。これにより、将来の変更によってこのエラー出力の挙動が意図せず変更されることを防ぎ、コードの品質を維持します。

関連リンク

参考にした情報源リンク