[インデックス 14819] ファイルの概要
このコミットは、Go言語の静的解析ツールである cmd/vet
における誤検知を修正するものです。具体的には、fmt
パッケージのフォーマット動詞 %q
に #
フラグを組み合わせた %#q
が、cmd/vet
によって誤って無効なフォーマットとして報告される問題を解決します。この修正により、%#q
を使用した fmt.Printf
などの呼び出しが正しく検証されるようになります。
コミット
- コミットハッシュ:
86aad668c0022f17a202fc8d4dd76a0456401048
- 作者: David Symonds dsymonds@golang.org
- コミット日時: 2013年1月7日 月曜日 15:31:51 +1100
- 変更ファイル:
src/cmd/vet/print.go
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/86aad668c0022f17a202fc8d4dd76a0456401048
元コミット内容
cmd/vet: %#q is a valid format (uses raw quotes).
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7057051
変更の背景
Go言語には、コードの潜在的なバグや疑わしい構造を検出するための静的解析ツール cmd/vet
があります。このツールは、fmt
パッケージの関数(Printf
など)のフォーマット文字列と引数の整合性をチェックする機能を持っています。
このコミットが行われる前は、cmd/vet
は fmt.Printf
などで %#q
というフォーマット動詞が使用された場合に、これを無効なフォーマットとして誤って報告していました。%#q
はGo言語の fmt
パッケージにおいて有効なフォーマットであり、「raw quotes」(生のリテラル表現)で値を引用符で囲んで出力するために使用されます。例えば、文字列 foo
を %#q
でフォーマットすると、"foo"
と出力されます。
cmd/vet
がこの有効なフォーマットを誤検知することは、開発者にとって不必要な警告となり、実際のバグを見逃す原因となる可能性がありました。このコミットは、cmd/vet
の内部ロジックを更新し、%#q
が正しく認識されるようにすることで、この誤検知を解消することを目的としています。
前提知識の解説
Go言語の fmt
パッケージとフォーマット動詞
Go言語の fmt
パッケージは、C言語の printf
に似た書式指定文字列を用いた入出力機能を提供します。フォーマット動詞は %
に続く文字で、値の表示形式を指定します。
%q
(quoted string): 文字列をGoのシンタックスで引用符で囲んで出力します。非表示文字はエスケープされます。例:fmt.Printf("%q", "hello\nworld")
は"hello\\nworld"
と出力されます。%#q
(raw quoted string):%q
と似ていますが、Goのバッククォート文字列リテラル(raw string literal)のように、可能な限りエスケープせずに引用符で囲んで出力します。これは、特に複数行の文字列や特殊文字を含む文字列をデバッグ目的で表示する際に便利です。例:fmt.Printf("%#q", "hello\nworld")
は`hello\nworld`
と出力されます。ただし、このコミットの文脈では、%#q
はstring
型に対しては"
で囲み、rune
やbyte
型に対してはシングルクォートで囲む動作を指しています。特に、string
型に対しては、%q
と同様にエスケープされた文字列をダブルクォートで囲んで出力しますが、#
フラグが付くことで、fmt
パッケージの内部的な処理で「raw quotes」として扱われるようになります。このコミットの修正は、cmd/vet
がこの#
フラグの存在を正しく認識するようにするためのものです。
cmd/vet
ツールの役割と目的
cmd/vet
は、Go言語のソースコードを静的に解析し、疑わしい構造や潜在的なエラーを検出するツールです。Goの標準ツールチェーンの一部として提供されており、以下のようなチェックを行います。
fmt
パッケージのフォーマット文字列の検証:fmt.Printf
などの関数呼び出しにおいて、フォーマット文字列と引数の型や数が一致しているかを確認します。例えば、fmt.Printf("%d", "hello")
のようなコードは、%d
が整数を期待しているのに文字列が渡されているため、vet
によって警告されます。- 構造体タグの検証:
json:"foo"
のような構造体タグの書式が正しいかを確認します。 - メソッドシグネチャの検証:
Error()
メソッドを持つ型がerror
インターフェースを正しく実装しているかなどを確認します。 - その他: 到達不能なコード、ロックの誤用、アトミック操作の誤用など、様々な潜在的な問題を検出します。
cmd/vet
は、コンパイルエラーにはならないが実行時に問題を引き起こす可能性のあるコードパターンを早期に発見し、コードの品質と信頼性を向上させるのに役立ちます。
Printf
系の関数の引数チェック
cmd/vet
が Printf
系の関数をチェックする際、その内部では各フォーマット動詞がどのようなフラグ(#
, +
, -
,
, 0
など)や幅、精度を許容するかという情報を持っています。この情報に基づいて、実際のフォーマット文字列がその動詞のルールに合致しているかを検証します。もし、許容されていないフラグが使われていたり、引数の型が期待と異なったりすると、vet
は警告を発します。
技術的詳細
このコミットの技術的な核心は、cmd/vet
が fmt
パッケージのフォーマット動詞の有効性をチェックする際に使用する内部データ構造 printVerbs
の更新にあります。
src/cmd/vet/print.go
ファイルは、cmd/vet
が fmt
パッケージのフォーマット文字列を解析し、その引数を検証するためのロジックを含んでいます。このファイル内には、printVerbs
という配列が定義されており、これは各フォーマット動詞(例: %d
, %s
, %q
)がどのようなフラグ(#
, +
, -
など)を許容するかを記述しています。
変更前は、%q
フォーマット動詞に対応するエントリは以下のようになっていました。
{'q', " -+.0"},
これは、%q
が
(スペース), -
(左寄せ), +
(符号), .
(精度), 0
(ゼロ埋め) の各フラグを許容することを意味します。しかし、Goの fmt
パッケージの仕様では、%q
は #
フラグも許容し、その場合は「raw quotes」として動作します。cmd/vet
のこの定義には #
フラグが含まれていなかったため、%#q
という有効なフォーマットが使われた際に、vet
は「無効なフラグが使われている」と誤解し、警告を発していました。
このコミットでは、%q
のエントリに #
フラグを追加することで、cmd/vet
が %#q
を有効なフォーマットとして正しく認識できるように修正しています。
{'q', " -+.0#"},
この変更により、cmd/vet
は fmt.Printf("%#q",
blah)
のようなコードを正しく解析し、警告を発しなくなります。これは、cmd/vet
の精度を向上させ、開発者がより正確な静的解析結果を得られるようにするための重要な修正です。
コアとなるコードの変更箇所
--- a/src/cmd/vet/print.go
+++ b/src/cmd/vet/print.go
@@ -208,7 +208,7 @@ var printVerbs = []printVerb{\
{'G', numFlag},\
{'o', sharpNumFlag},\
{'p', \"-#\"},\
-\t{'q', \" -+.0\"},\
+\t{'q', \" -+.0#\"},\
{'s', \" -+.0\"},\
{'t', \"-\"},\
{'T', \"-\"},\
@@ -287,6 +287,7 @@ func BadFunctionUsedInTests() {\
fmt.Printf(\"%.*d\", 3, 3) // correct
fmt.Printf(\"%.*d\", 3, 3, 3) // ERROR \"wrong number of args in Printf call\"
fmt.Printf(\"%q %q\", multi()...) // ok
+\tfmt.Printf(\"%#q\", `blah`) // ok
printf(\"now is the time\", \"buddy\") // ERROR \"no formatting directive\"
Printf(\"now is the time\", \"buddy\") // ERROR \"no formatting directive\"
Printf(\"hi\") // ok
コアとなるコードの解説
変更の中心は src/cmd/vet/print.go
ファイル内の printVerbs
配列の定義です。
printVerbs
は printVerb
型の構造体のスライスであり、各要素は特定のフォーマット動詞とその動詞が許容するフラグのセットを定義しています。
{'q', " -+.0"}
: この行は、フォーマット動詞%q
が、スペース、マイナス、プラス、ピリオド、ゼロの各フラグを許容することを定義していました。{'q', " -+.0#"}
: 修正後の行です。既存のフラグに加えて、#
フラグが追加されています。これにより、cmd/vet
は%q
動詞が#
フラグと組み合わせて使用されることを正しく認識し、誤検知を回避できるようになります。
また、コミットの差分には、テストケースの追加も示されています。
fmt.Printf("%#q",
blah) // ok
: この行は、%#q
を使用したPrintf
呼び出しが、cmd/vet
によって「ok」(エラーなし)と判断されるべきであることを示すテストケースです。このテストケースの追加は、修正が意図通りに機能することを確認するために重要です。
この修正は、cmd/vet
の内部的なフォーマット動詞の定義をGo言語の fmt
パッケージの実際の動作と一致させることで、ツールの正確性を向上させています。
関連リンク
- Go CL (Change List): https://golang.org/cl/7057051 このリンクは、このコミットに対応するGoのコードレビューシステム(Gerrit)上の変更リストを示しています。通常、Goプロジェクトのコミットは、まずこのシステムでレビューされ、承認されてからメインリポジトリにマージされます。
参考にした情報源リンク
- Go fmt パッケージのドキュメント:
- https://pkg.go.dev/fmt
- 特に、
%q
および#
フラグに関する記述を参照しました。
- Go cmd/vet のドキュメント:
- https://pkg.go.dev/cmd/vet
cmd/vet
の目的と機能について理解するために参照しました。
- Go言語のソースコード (src/cmd/vet/print.go):
- https://github.com/golang/go/blob/master/src/cmd/vet/print.go
printVerbs
の定義とcmd/vet
のフォーマット検証ロジックを直接確認するために参照しました。
- Go言語のバッククォート文字列リテラル (raw string literal):
- https://go.dev/ref/spec#String_literals
%#q
の「raw quotes」の概念を理解するために参照しました。
[インデックス 14819] ファイルの概要
このコミットは、Go言語の静的解析ツールである cmd/vet
における誤検知を修正するものです。具体的には、fmt
パッケージのフォーマット動詞 %q
に #
フラグを組み合わせた %#q
が、cmd/vet
によって誤って無効なフォーマットとして報告される問題を解決します。この修正により、%#q
を使用した fmt.Printf
などの呼び出しが正しく検証されるようになります。
コミット
- コミットハッシュ:
86aad668c0022f17a202fc8d4dd76a0456401048
- 作者: David Symonds dsymonds@golang.org
- コミット日時: 2013年1月7日 月曜日 15:31:51 +1100
- 変更ファイル:
src/cmd/vet/print.go
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/86aad668c0022f17a202fc8d4dd76a0456401048
元コミット内容
cmd/vet: %#q is a valid format (uses raw quotes).
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7057051
変更の背景
Go言語には、コードの潜在的なバグや疑わしい構造を検出するための静的解析ツール cmd/vet
があります。このツールは、fmt
パッケージの関数(Printf
など)のフォーマット文字列と引数の整合性をチェックする機能を持っています。
このコミットが行われる前は、cmd/vet
は fmt.Printf
などで %#q
というフォーマット動詞が使用された場合に、これを無効なフォーマットとして誤って報告していました。%#q
はGo言語の fmt
パッケージにおいて有効なフォーマットであり、「raw quotes」(生のリテラル表現)で値を引用符で囲んで出力するために使用されます。例えば、文字列 foo
を %#q
でフォーマットすると、"foo"
と出力されます。
cmd/vet
がこの有効なフォーマットを誤検知することは、開発者にとって不必要な警告となり、実際のバグを見逃す原因となる可能性がありました。このコミットは、cmd/vet
の内部ロジックを更新し、%#q
が正しく認識されるようにすることで、この誤検知を解消することを目的としています。
前提知識の解説
Go言語の fmt
パッケージとフォーマット動詞
Go言語の fmt
パッケージは、C言語の printf
に似た書式指定文字列を用いた入出力機能を提供します。フォーマット動詞は %
に続く文字で、値の表示形式を指定します。
-
%q
(quoted string): この動詞は、文字列をGoのシンタックスで二重引用符で囲み、特殊文字(バックスラッシュや二重引用符など)をエスケープして出力します。これにより、出力された文字列はGoの有効な文字列リテラルとなり、Goコードに直接コピー&ペーストして使用できます。- 例:
fmt.Printf("%q", "hello\nworld")
は"hello\\nworld"
と出力されます。
- 例:
-
%#q
(backquoted string/raw string literal): この動詞も文字列をGoの文字列リテラルとしてフォーマットしますが、可能な場合はバッククォート(`
)を使用した生文字列リテラル(raw string literal)を試みます。生文字列リテラルはバックスラッシュエスケープを解釈しません。文字列にバッククォートが含まれる場合は、二重引用符で囲まれたエスケープされた文字列にフォールバックします。- 例:
fmt.Printf("%#q", "hello\nworld")
は`hello\nworld`
と出力されます。 - これは、特に文字列がエスケープを必要とする特殊文字を含む場合に非常に便利です。
- 例:
本質的に、%q
は「解釈された文字列リテラル」("
とエスケープを含む)を生成し、%#q
は可能な限り「生文字列リテラル」(`
を含む)を目指します。どちらの出力もGoのソースコードで直接使用可能です。
cmd/vet
ツールの役割と目的
cmd/vet
は、Go言語のソースコードを静的に解析し、疑わしい構造や潜在的なエラーを検出するツールです。Goの標準ツールチェーンの一部として提供されており、以下のようなチェックを行います。
fmt
パッケージのフォーマット文字列の検証:fmt.Printf
などの関数呼び出しにおいて、フォーマット文字列と引数の型や数が一致しているかを確認します。例えば、fmt.Printf("%d", "hello")
のようなコードは、%d
が整数を期待しているのに文字列が渡されているため、vet
によって警告されます。- 構造体タグの検証:
json:"foo"
のような構造体タグの書式が正しいかを確認します。 - メソッドシグネチャの検証:
Error()
メソッドを持つ型がerror
インターフェースを正しく実装しているかなどを確認します。 - その他: 到達不能なコード、ロックの誤用、アトミック操作の誤用など、様々な潜在的な問題を検出します。
cmd/vet
は、コンパイルエラーにはならないが実行時に問題を引き起こす可能性のあるコードパターンを早期に発見し、コードの品質と信頼性を向上させるのに役立ちます。
Printf
系の関数の引数チェック
cmd/vet
が Printf
系の関数をチェックする際、その内部では各フォーマット動詞がどのようなフラグ(#
, +
, -
,
, 0
など)や幅、精度を許容するかという情報を持っています。この情報に基づいて、実際のフォーマット文字列がその動詞のルールに合致しているかを検証します。もし、許容されていないフラグが使われていたり、引数の型が期待と異なったりすると、vet
は警告を発します。
技術的詳細
このコミットの技術的な核心は、cmd/vet
が fmt
パッケージのフォーマット動詞の有効性をチェックする際に使用する内部データ構造 printVerbs
の更新にあります。
src/cmd/vet/print.go
ファイルは、cmd/vet
が fmt
パッケージのフォーマット文字列を解析し、その引数を検証するためのロジックを含んでいます。このファイル内には、printVerbs
という配列が定義されており、これは各フォーマット動詞(例: %d
, %s
, %q
)がどのようなフラグ(#
, +
, -
など)を許容するかを記述しています。
変更前は、%q
フォーマット動詞に対応するエントリは以下のようになっていました。
{'q', " -+.0"},
これは、%q
が
(スペース), -
(左寄せ), +
(符号), .
(精度), 0
(ゼロ埋め) の各フラグを許容することを意味します。しかし、Goの fmt
パッケージの仕様では、%q
は #
フラグも許容し、その場合は「raw quotes」として動作します。cmd/vet
のこの定義には #
フラグが含まれていなかったため、%#q
という有効なフォーマットが使われた際に、vet
は「無効なフラグが使われている」と誤解し、警告を発していました。
このコミットでは、%q
のエントリに #
フラグを追加することで、cmd/vet
が %#q
を有効なフォーマットとして正しく認識できるように修正しています。
{'q', " -+.0#"},
この変更により、cmd/vet
は fmt.Printf("%#q",
blah)
のようなコードを正しく解析し、警告を発しなくなります。これは、cmd/vet
の精度を向上させ、開発者がより正確な静的解析結果を得られるようにするための重要な修正です。
コアとなるコードの変更箇所
--- a/src/cmd/vet/print.go
+++ b/src/cmd/vet/print.go
@@ -208,7 +208,7 @@ var printVerbs = []printVerb{\
{'G', numFlag},\
{'o', sharpNumFlag},\
{'p', \"-#\"},\
-\t{'q', \" -+.0\"},\
+\t{'q', \" -+.0#\"},\
{'s', \" -+.0\"},\
{'t', \"-\"},\
{'T', \"-\"},\
@@ -287,6 +287,7 @@ func BadFunctionUsedInTests() {\
fmt.Printf(\"%.*d\", 3, 3) // correct
fmt.Printf(\"%.*d\", 3, 3, 3) // ERROR \"wrong number of args in Printf call\"
fmt.Printf(\"%q %q\", multi()...) // ok
+\tfmt.Printf(\"%#q\", `blah`) // ok
printf(\"now is the time\", \"buddy\") // ERROR \"no formatting directive\"
Printf(\"now is the time\", \"buddy\") // ERROR \"no formatting directive\"
Printf(\"hi\") // ok
コアとなるコードの解説
変更の中心は src/cmd/vet/print.go
ファイル内の printVerbs
配列の定義です。
printVerbs
は printVerb
型の構造体のスライスであり、各要素は特定のフォーマット動詞とその動詞が許容するフラグのセットを定義しています。
{'q', " -+.0"}
: この行は、フォーマット動詞%q
が、スペース、マイナス、プラス、ピリオド、ゼロの各フラグを許容することを定義していました。{'q', " -+.0#"}
: 修正後の行です。既存のフラグに加えて、#
フラグが追加されています。これにより、cmd/vet
は%q
動詞が#
フラグと組み合わせて使用されることを正しく認識し、誤検知を回避できるようになります。
また、コミットの差分には、テストケースの追加も示されています。
fmt.Printf("%#q",
blah) // ok
: この行は、%#q
を使用したPrintf
呼び出しが、cmd/vet
によって「ok」(エラーなし)と判断されるべきであることを示すテストケースです。このテストケースの追加は、修正が意図通りに機能することを確認するために重要です。
この修正は、cmd/vet
の内部的なフォーマット動詞の定義をGo言語の fmt
パッケージの実際の動作と一致させることで、ツールの正確性を向上させています。
関連リンク
- Go CL (Change List): https://golang.org/cl/7057051 このリンクは、このコミットに対応するGoのコードレビューシステム(Gerrit)上の変更リストを示しています。通常、Goプロジェクトのコミットは、まずこのシステムでレビューされ、承認されてからメインリポジトリにマージされます。
参考にした情報源リンク
- Go fmt パッケージのドキュメント:
- https://pkg.go.dev/fmt
- 特に、
%q
および#
フラグに関する記述を参照しました。
- Go cmd/vet のドキュメント:
- https://pkg.go.dev/cmd/vet
cmd/vet
の目的と機能について理解するために参照しました。
- Go言語のソースコード (src/cmd/vet/print.go):
- https://github.com/golang/go/blob/master/src/cmd/vet/print.go
printVerbs
の定義とcmd/vet
のフォーマット検証ロジックを直接確認するために参照しました。
- Go言語のバッククォート文字列リテラル (raw string literal):
- https://go.dev/ref/spec#String_literals
%#q
の「raw quotes」の概念を理解するために参照しました。